1#![warn(missing_docs)]
2
3use console;
4use glob;
5use nargo_ir::{JsExpr, JsStmt, TseAttribute};
6use nargo_parser::{Parser, ParserRegistry};
7use nargo_script_analyzer::ScriptAnalyzer;
8use nargo_types::{Error, Result, Result as NargoResult};
9use regex::Regex;
10use serde::{Deserialize, Serialize};
11use std::{
12 collections::{HashMap, HashSet},
13 fs::File,
14 io::Read,
15 path::PathBuf,
16 sync::Arc,
17};
18use walkdir::WalkDir;
19
20use crate::modules::{
22 check::ExpressionChecker,
23 module::ModuleResolver,
24 template::TemplateChecker,
25 types::{TsConfig, TypeEnv},
26 utils::Utils,
27};
28
29mod modules;
31
32pub use modules::types::{Type, TypeCheckResult, TypeError};
34pub use modules::typescript::TypeScriptStmtHandler;
35
36#[derive(Debug, Clone)]
38pub struct FileCache {
39 pub modified_time: std::time::SystemTime,
41 pub type_env: TypeEnv,
43 pub dependencies: Vec<String>,
45 pub dependents: Vec<String>,
47}
48
49pub struct TypeChecker {
53 registry: Arc<ParserRegistry>,
55 analyzer: ScriptAnalyzer,
57 type_env: TypeEnv,
59 errors: Vec<TypeError>,
61 config: Option<TsConfig>,
63 modules: HashMap<String, TypeEnv>,
65 parsing_modules: HashSet<String>,
67 current_file: Option<String>,
69 file_cache: HashMap<String, FileCache>,
71 file_dependencies: HashMap<String, Vec<String>>,
73}
74
75impl TypeChecker {
76 pub fn new() -> Self {
78 let mut registry = ParserRegistry::new();
80
81 let template_parser = Arc::new(nargo_parser::OakVueTemplateParser);
83 registry.register_template_parser("nargo", template_parser.clone());
84 registry.register_template_parser("html", template_parser);
85
86 let ts_parser = Arc::new(nargo_parser::OakTypeScriptParser);
87 registry.register_script_parser("ts", ts_parser.clone());
88 registry.register_script_parser("typescript", ts_parser.clone());
89 registry.register_script_parser("js", ts_parser.clone());
90 registry.register_script_parser("javascript", ts_parser);
91
92 let css_parser = Arc::new(nargo_parser::OakCssParser);
93 registry.register_style_parser("css", css_parser.clone());
94 registry.register_style_parser("tailwind", css_parser);
95
96 let mut type_env = TypeEnv::new();
98
99 type_env.add_variable("undefined".to_string(), Type::Void);
101 type_env.add_variable("null".to_string(), Type::Void);
102 type_env.add_variable("true".to_string(), Type::Boolean);
103 type_env.add_variable("false".to_string(), Type::Boolean);
104
105 Self { registry: Arc::new(registry), analyzer: ScriptAnalyzer::new(), type_env, errors: Vec::new(), config: None, modules: HashMap::new(), parsing_modules: HashSet::new(), current_file: None, file_cache: HashMap::new(), file_dependencies: HashMap::new() }
106 }
107
108 pub fn parse_tsconfig(&mut self, path: &PathBuf) -> NargoResult<()> {
118 let content = std::fs::read_to_string(path).map_err(Error::io_error)?;
119 let config: TsConfig = serde_json::from_str(&content).map_err(|e| Error::parse_error(format!("解析 tsconfig.json 失败: {}", e), nargo_types::Span::unknown()))?;
120 self.config = Some(config);
121 Ok(())
122 }
123
124 pub fn load_tsconfig(&mut self, dir: &PathBuf) -> NargoResult<()> {
134 let tsconfig_path = dir.join("tsconfig.json");
135 if tsconfig_path.exists() {
136 self.parse_tsconfig(&tsconfig_path)?;
137 }
138 Ok(())
139 }
140
141 pub fn check_file(&mut self, file: &PathBuf, source: &str) -> NargoResult<()> {
152 let name = file.file_stem().unwrap_or_default().to_string_lossy().to_string();
153 let file_path_str = file.to_string_lossy().to_string();
154
155 if let Ok(metadata) = file.metadata() {
157 if let Ok(modified_time) = metadata.modified() {
158 if let Some(cache) = self.file_cache.get(&file_path_str) {
159 if cache.modified_time == modified_time {
160 self.type_env = cache.type_env.clone();
162 return Ok(());
163 }
164 }
165 }
166 }
167
168 self.current_file = Some(file_path_str.clone());
170
171 self.errors.clear();
173
174 let mut parser = Parser::new(name, source, self.registry.clone());
176 let ir = parser.parse_all()?;
177
178 if let Some(script) = &ir.script {
180 let _meta = self.analyzer.analyze(script)?;
182
183 for stmt in &script.body {
185 self.check_statement_with_module(stmt, &file_path_str)?;
186 }
187 }
188
189 if let Some(template) = &ir.template {
191 self.check_template_impl(template);
192 }
193
194 if !self.errors.is_empty() {
196 let error_messages: Vec<String> = self
198 .errors
199 .iter()
200 .map(|e| {
201 let file_name = e.file_name.as_ref().unwrap_or(&file_path_str);
202 let line = e.line.unwrap_or(0);
203 let column = e.column.unwrap_or(0);
204 let default_error_code = "TS0000".to_string();
205 let error_code = e.error_code.as_ref().unwrap_or(&default_error_code);
206 let mut message = format!("{}:{}:{} - error {}: {}", file_name, line, column, error_code, e.message);
207
208 if let Some(related_types) = &e.related_types {
210 if !related_types.is_empty() {
211 message.push_str(&format!("\n 相关类型: {}", related_types.join(", ")));
212 }
213 }
214
215 if let Some(suggestion) = &e.suggestion {
217 message.push_str(&format!("\n 建议: {}", suggestion));
218 }
219
220 message
221 })
222 .collect();
223 return Err(Error::parse_error(error_messages.join("\n"), nargo_types::Span::unknown()));
224 }
225
226 let dependencies = self.collect_file_dependencies(&file_path_str);
228
229 self.update_file_dependencies(&file_path_str, &dependencies);
231
232 if let Ok(metadata) = file.metadata() {
234 if let Ok(modified_time) = metadata.modified() {
235 let cache = FileCache { modified_time, type_env: self.type_env.clone(), dependencies: dependencies.clone(), dependents: self.get_file_dependents(&file_path_str) };
236 self.file_cache.insert(file_path_str.clone(), cache);
237 }
238 }
239
240 self.current_file = None;
242
243 Ok(())
244 }
245
246 pub async fn check_project(&mut self, input: PathBuf) -> NargoResult<TypeCheckResult> {
256 self.load_tsconfig(&input)?;
258
259 let files = self.find_frontend_files(&input)?;
260 let mut errors = 0;
261 let checked_files = files.len();
262
263 use rayon::prelude::*;
265 let results: Vec<(PathBuf, std::result::Result<(), Error>)> = files
266 .par_iter()
267 .map(|file| {
268 let source = std::fs::read_to_string(file).map_err(Error::io_error);
269 match source {
270 Ok(source) => {
271 let mut checker = TypeChecker::new();
272 checker.load_tsconfig(&input).unwrap();
273 (file.clone(), checker.check_file(file, &source))
274 }
275 Err(e) => (file.clone(), Err(e)),
276 }
277 })
278 .collect();
279
280 for (file, result) in results {
282 if let Err(e) = result {
283 eprintln!("{} {}: {}", console::style("✘").red(), file.display(), e);
284 errors += 1;
285 }
286 }
287
288 Ok(TypeCheckResult { errors, checked_files })
289 }
290}
291
292impl ModuleResolver for TypeChecker {
294 fn resolve_import(&mut self, source: &str, specifiers: &[String], current_file: &PathBuf) -> NargoResult<()> {
295 let module_path = self.resolve_module_path(source, current_file)?;
297
298 if self.parsing_modules.contains(&module_path) {
300 return Err(Error::parse_error("循环依赖检测到".to_string(), nargo_types::Span::unknown()));
301 }
302
303 if self.modules.contains_key(&module_path) {
305 let module_env = self.modules.get(&module_path).unwrap().clone();
307 self.import_module_types(&module_env, specifiers);
309 return Ok(());
310 }
311
312 self.parsing_modules.insert(module_path.clone());
314
315 let source_code = std::fs::read_to_string(&module_path).map_err(Error::io_error)?;
317 let module_path_buf = PathBuf::from(&module_path);
318 let module_name = module_path_buf.file_stem().unwrap_or_default().to_string_lossy().to_string();
319
320 let mut parser = Parser::new(module_name, &source_code, self.registry.clone());
321 let ir = parser.parse_all()?;
322
323 let mut module_env = TypeEnv::new();
325
326 if let Some(script) = &ir.script {
328 let _meta = self.analyzer.analyze(script)?;
330
331 for stmt in &script.body {
333 self.check_statement_for_module(stmt, &mut module_env, &module_path)?;
334 }
335 }
336
337 let module_env_clone = module_env.clone();
339 self.modules.insert(module_path.clone(), module_env_clone);
340
341 self.import_module_types(&module_env, specifiers);
343
344 self.parsing_modules.remove(&module_path);
346
347 Ok(())
348 }
349
350 fn resolve_module_path(&self, source: &str, current_file: &PathBuf) -> NargoResult<String> {
351 let current_dir = current_file.parent().unwrap_or_else(|| std::path::Path::new("."));
352
353 if source.starts_with("./") || source.starts_with("../") {
355 let module_path = current_dir.join(source);
356 let extensions = [".ts", ".tsx", ".js", ".jsx"];
358
359 for ext in &extensions {
360 let path_with_ext = module_path.with_extension(ext.trim_start_matches('.'));
361 if path_with_ext.exists() {
362 return Ok(path_with_ext.to_string_lossy().to_string());
363 }
364 }
365
366 let index_path = module_path.join("index");
368 for ext in &extensions {
369 let index_with_ext = index_path.with_extension(ext.trim_start_matches('.'));
370 if index_with_ext.exists() {
371 return Ok(index_with_ext.to_string_lossy().to_string());
372 }
373 }
374
375 return Err(Error::parse_error(format!("模块未找到: {}", source), nargo_types::Span::unknown()));
376 }
377
378 if let Some(config) = &self.config {
380 if let Some(compiler_options) = &config.compiler_options {
381 if let Some(paths) = &compiler_options.paths {
383 for (pattern, substitutions) in paths {
384 if source.starts_with(pattern) {
386 for substitution in substitutions {
387 let substituted_path = source.replace(pattern, substitution);
388 let base_path = if let Some(base_url) = &compiler_options.base_url { current_dir.join(base_url) } else { current_dir.to_path_buf() };
389 let module_path = base_path.join(substituted_path);
390
391 let extensions = [".ts", ".tsx", ".js", ".jsx"];
393
394 for ext in &extensions {
395 let path_with_ext = module_path.with_extension(ext.trim_start_matches('.'));
396 if path_with_ext.exists() {
397 return Ok(path_with_ext.to_string_lossy().to_string());
398 }
399 }
400
401 let index_path = module_path.join("index");
403 for ext in &extensions {
404 let index_with_ext = index_path.with_extension(ext.trim_start_matches('.'));
405 if index_with_ext.exists() {
406 return Ok(index_with_ext.to_string_lossy().to_string());
407 }
408 }
409 }
410 }
411 }
412 }
413
414 if let Some(base_url) = &compiler_options.base_url {
416 let base_path = current_dir.join(base_url);
417 let module_path = base_path.join(source);
418
419 let extensions = [".ts", ".tsx", ".js", ".jsx"];
421
422 for ext in &extensions {
423 let path_with_ext = module_path.with_extension(ext.trim_start_matches('.'));
424 if path_with_ext.exists() {
425 return Ok(path_with_ext.to_string_lossy().to_string());
426 }
427 }
428
429 let index_path = module_path.join("index");
431 for ext in &extensions {
432 let index_with_ext = index_path.with_extension(ext.trim_start_matches('.'));
433 if index_with_ext.exists() {
434 return Ok(index_with_ext.to_string_lossy().to_string());
435 }
436 }
437 }
438 }
439 }
440
441 let node_modules_path = current_dir.join("node_modules").join(source);
443 let extensions = [".ts", ".tsx", ".js", ".jsx"];
444
445 for ext in &extensions {
446 let path_with_ext = node_modules_path.with_extension(ext.trim_start_matches('.'));
447 if path_with_ext.exists() {
448 return Ok(path_with_ext.to_string_lossy().to_string());
449 }
450 }
451
452 let index_path = node_modules_path.join("index");
454 for ext in &extensions {
455 let index_with_ext = index_path.with_extension(ext.trim_start_matches('.'));
456 if index_with_ext.exists() {
457 return Ok(index_with_ext.to_string_lossy().to_string());
458 }
459 }
460
461 Err(Error::parse_error(format!("不支持的模块路径: {}", source), nargo_types::Span::unknown()))
463 }
464
465 fn check_statement_for_module(&mut self, stmt: &nargo_ir::JsStmt, module_env: &mut TypeEnv, module_path: &str) -> NargoResult<()> {
466 match stmt {
467 nargo_ir::JsStmt::Import { source, specifiers, .. } => {
468 let current_file = PathBuf::from(module_path);
470 self.resolve_import(source, specifiers, ¤t_file)?;
471 }
472 nargo_ir::JsStmt::Export { declaration, .. } => {
473 self.check_statement(declaration);
475 self.add_declaration_to_module(declaration, module_env);
477 }
478 nargo_ir::JsStmt::ExportAll { source, .. } => {
479 let current_file = PathBuf::from(module_path);
481 self.resolve_import(source, &["*".to_string()], ¤t_file)?;
482 }
483 nargo_ir::JsStmt::ExportNamed { source, specifiers, .. } => {
484 if let Some(source) = source {
486 let current_file = PathBuf::from(module_path);
487 self.resolve_import(source, specifiers, ¤t_file)?;
488 }
489 }
491 _ => {
492 self.check_statement(stmt);
494 }
495 }
496 Ok(())
497 }
498
499 fn add_declaration_to_module(&self, stmt: &nargo_ir::JsStmt, module_env: &mut TypeEnv) {
500 match stmt {
501 nargo_ir::JsStmt::FunctionDecl { id, params, body, .. } => {
502 let mut param_types = Vec::new();
504 for _param in params {
505 param_types.push(Type::Any);
506 }
507
508 let return_type = Type::Any;
510
511 let func_type = Type::Function(param_types, Box::new(return_type));
513
514 module_env.add_function(id.clone(), func_type);
516 }
517 nargo_ir::JsStmt::VariableDecl { id, init, .. } => {
518 let mut var_type = Type::Any;
520
521 if let Some(init_expr) = init {
523 }
525
526 module_env.add_variable(id.clone(), var_type);
528 }
529 _ => {
530 }
532 }
533 }
534
535 fn import_module_types(&mut self, module_env: &TypeEnv, specifiers: &[String]) {
536 for specifier in specifiers {
537 if specifier == "*" {
538 for (name, ty) in &module_env.variables {
540 self.type_env.add_variable(name.clone(), ty.clone());
541 }
542 for (name, ty) in &module_env.functions {
543 self.type_env.add_function(name.clone(), ty.clone());
544 }
545 }
546 else {
547 if let Some(ty) = module_env.get_variable(specifier) {
549 self.type_env.add_variable(specifier.clone(), ty.clone());
550 }
551 if let Some(ty) = module_env.get_function(specifier) {
552 self.type_env.add_function(specifier.clone(), ty.clone());
553 }
554 }
555 }
556 }
557
558 fn check_statement_with_module(&mut self, stmt: &nargo_ir::JsStmt, file_path: &str) -> NargoResult<()> {
559 let file_name = PathBuf::from(file_path).file_name().unwrap_or_default().to_string_lossy().to_string();
561
562 match stmt {
563 nargo_ir::JsStmt::Import { source, specifiers, .. } => {
564 let current_file = PathBuf::from(file_path);
566 self.resolve_import(source, specifiers, ¤t_file)?;
567 }
568 nargo_ir::JsStmt::Export { declaration, .. } => {
569 self.check_statement(declaration);
571 self.add_declaration_to_current_env(declaration);
573 }
574 nargo_ir::JsStmt::ExportAll { source, .. } => {
575 let current_file = PathBuf::from(file_path);
577 self.resolve_import(source, &["*".to_string()], ¤t_file)?;
578 }
579 nargo_ir::JsStmt::ExportNamed { source, specifiers, .. } => {
580 if let Some(source) = source {
582 let current_file = PathBuf::from(file_path);
583 self.resolve_import(source, specifiers, ¤t_file)?;
584 }
585 }
587 _ => {
588 self.check_statement(stmt);
590 }
591 }
592 Ok(())
593 }
594
595 fn add_declaration_to_current_env(&mut self, stmt: &nargo_ir::JsStmt) {
596 match stmt {
597 nargo_ir::JsStmt::FunctionDecl { id, params, .. } => {
598 let mut param_types = Vec::new();
600 for _param in params {
601 param_types.push(Type::Any);
602 }
603
604 let return_type = Type::Any;
606
607 let func_type = Type::Function(param_types, Box::new(return_type));
609
610 self.type_env.add_function(id.clone(), func_type);
612 }
613 nargo_ir::JsStmt::VariableDecl { id, .. } => {
614 let var_type = Type::Any;
616
617 self.type_env.add_variable(id.clone(), var_type);
619 }
620 _ => {
621 }
623 }
624 }
625}
626
627impl TypeScriptStmtHandler for TypeChecker {
629 fn handle_typescript_stmt(&mut self, stmt_str: &str) {
630 if stmt_str.starts_with("interface ") {
632 self.handle_interface_stmt(stmt_str);
634 }
635 else if stmt_str.starts_with("type ") {
636 self.handle_type_alias_stmt(stmt_str);
638 }
639 }
640
641 fn handle_interface_def(&mut self, name: String, members: HashMap<String, Type>, extends: Vec<String>) {
642 self.type_env.add_interface(name, members, extends);
643 }
644
645 fn handle_type_alias(&mut self, name: String, ty: Type) {
646 self.type_env.add_type_alias(name, ty);
647 }
648
649 fn handle_generic_type(&self, name: String, type_args: Vec<Type>) -> Type {
650 Type::GenericInterface(name, type_args)
652 }
653
654 fn handle_union_type(&self, types: Vec<Type>) -> Type {
655 Type::Union(types)
656 }
657
658 fn handle_intersection_type(&self, types: Vec<Type>) -> Type {
659 Type::Intersection(types)
660 }
661
662 fn handle_conditional_type(&self, check_type: Type, extends_type: Type, true_type: Type, false_type: Type) -> Type {
663 Type::Conditional(Box::new(check_type), Box::new(extends_type), Box::new(true_type), Box::new(false_type))
664 }
665
666 fn handle_mapped_type(&self, key_var: String, source_type: Type, mapped_type: Type) -> Type {
667 Type::Mapped(key_var, Box::new(source_type), Box::new(mapped_type))
668 }
669
670 fn handle_index_access_type(&self, object_type: Type, index_type: Type) -> Type {
671 Type::IndexAccess(Box::new(object_type), Box::new(index_type))
672 }
673
674 fn handle_keyof_type(&self, target_type: Type) -> Type {
675 Type::KeyOf(Box::new(target_type))
676 }
677
678 fn handle_literal_type(&self, literal: String) -> Type {
679 Type::Literal(literal)
680 }
681
682 fn handle_this_type(&self, this_type: Type) -> Type {
683 Type::ThisType(Box::new(this_type))
684 }
685
686 fn handle_omit_this_parameter_type(&self, target_type: Type) -> Type {
687 Type::OmitThisParameter(Box::new(target_type))
688 }
689
690 fn handle_this_parameter_type(&self, target_type: Type) -> Type {
691 Type::ThisParameterType(Box::new(target_type))
692 }
693
694 fn handle_interface_stmt(&mut self, stmt_str: &str) {
695 let re = Regex::new(r"interface\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*(?:extends\s+([a-zA-Z_][a-zA-Z0-9_]*(?:\s*,\s*[a-zA-Z_][a-zA-Z0-9_]*)*)?)?\s*\{([\s\S]*?)\}").unwrap();
698 if let Some(captures) = re.captures(stmt_str) {
699 let name = captures[1].to_string();
700 let extends = if let Some(extends_str) = captures.get(2) { extends_str.as_str().split(",").map(|s| s.trim().to_string()).collect() } else { Vec::new() };
701 let members_str = captures[3].to_string();
702
703 let members = self.parse_interface_members(&members_str);
705
706 self.handle_interface_def(name, members, extends);
708 }
709 }
710
711 fn parse_interface_members(&self, members_str: &str) -> HashMap<String, Type> {
712 let mut members = HashMap::new();
713
714 let member_re = Regex::new(r"([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*([a-zA-Z_][a-zA-Z0-9_]*);").unwrap();
716 for capture in member_re.captures_iter(members_str) {
717 let name = capture[1].to_string();
718 let type_str = capture[2].to_string();
719 let ty = self.parse_type(&type_str);
720 members.insert(name, ty);
721 }
722
723 members
724 }
725
726 fn handle_type_alias_stmt(&mut self, stmt_str: &str) {
727 let re = Regex::new(r"type\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*([\s\S]*?);").unwrap();
730 if let Some(captures) = re.captures(stmt_str) {
731 let name = captures[1].to_string();
732 let type_str = captures[2].to_string();
733 let ty = self.parse_type(&type_str);
734 self.handle_type_alias(name, ty);
735 }
736 }
737
738 fn parse_type(&self, type_str: &str) -> Type {
739 let type_str = type_str.trim();
740
741 match type_str {
743 "any" => Type::Any,
744 "void" => Type::Void,
745 "never" => Type::Never,
746 "unknown" => Type::Unknown,
747 "number" => Type::Number,
748 "string" => Type::String,
749 "boolean" => Type::Boolean,
750 "symbol" => Type::Symbol,
751 "bigint" => Type::BigInt,
752 _ => {
753 if let Some((name, type_args)) = self.parse_generic_type(type_str) {
755 match name.as_str() {
757 "Partial" => return self.handle_partial_type(type_args),
758 "Required" => return self.handle_required_type(type_args),
759 "Pick" => return self.handle_pick_type(type_args),
760 "Omit" => return self.handle_omit_type(type_args),
761 "Record" => return self.handle_record_type(type_args),
762 "Exclude" => return self.handle_exclude_type(type_args),
763 "Extract" => return self.handle_extract_type(type_args),
764 "NonNullable" => return self.handle_non_nullable_type(type_args),
765 "Parameters" => return self.handle_parameters_type(type_args),
766 "ReturnType" => return self.handle_return_type(type_args),
767 "InstanceType" => return self.handle_instance_type(type_args),
768 "ThisType" => return self.handle_this_type(type_args),
769 "OmitThisParameter" => return self.handle_omit_this_parameter_type(type_args),
770 "ThisParameterType" => return self.handle_this_parameter_type(type_args),
771 _ => return Type::GenericInterface(name, type_args),
772 }
773 }
774
775 if type_str.ends_with("[]") {
777 let elem_type_str = type_str.trim_end_matches("[]");
779 let elem_type = self.parse_type(elem_type_str);
780 Type::Array(Box::new(elem_type))
781 }
782 else if type_str.contains("|") {
783 let types = type_str.split("|").map(|t| self.parse_type(t.trim())).collect();
785 Type::Union(types)
786 }
787 else if type_str.contains("&") {
788 let types = type_str.split("&").map(|t| self.parse_type(t.trim())).collect();
790 Type::Intersection(types)
791 }
792 else if type_str.starts_with("{") && type_str.ends_with("}") {
793 let content = &type_str[1..type_str.len() - 1];
795 if content.contains("in") {
796 let parts: Vec<&str> = content.split(":").collect();
799 if parts.len() == 2 {
800 let key_part = parts[0].trim();
801 let value_type_str = parts[1].trim();
802
803 let key_re = Regex::new(r"\[(.*?)\s+in\s+(.*?)\]").unwrap();
805 if let Some(captures) = key_re.captures(key_part) {
806 let key_var = captures[1].trim().to_string();
807 let source_type_str = captures[2].trim();
808 let source_type = self.parse_type(source_type_str);
809 let mapped_type = self.parse_type(value_type_str);
810 return self.handle_mapped_type(key_var, source_type, mapped_type);
811 }
812 }
813 let members = self.parse_interface_members(content);
815 Type::Object(members)
816 }
817 else {
818 let members = self.parse_interface_members(content);
820 Type::Object(members)
821 }
822 }
823 else if type_str.contains("extends") && type_str.contains("?") && type_str.contains(":") {
824 let parts: Vec<&str> = type_str.split("?").collect();
827 if parts.len() == 2 {
828 let extends_part = parts[0].trim();
829 let result_parts: Vec<&str> = parts[1].split(":").collect();
830 if result_parts.len() == 2 {
831 let true_type_str = result_parts[0].trim();
832 let false_type_str = result_parts[1].trim();
833
834 let extends_parts: Vec<&str> = extends_part.split("extends").collect();
835 if extends_parts.len() == 2 {
836 let check_type_str = extends_parts[0].trim();
837 let extends_type_str = extends_parts[1].trim();
838
839 let check_type = self.parse_type(check_type_str);
840 let extends_type = self.parse_type(extends_type_str);
841 let true_type = self.parse_type(true_type_str);
842 let false_type = self.parse_type(false_type_str);
843
844 return self.handle_conditional_type(check_type, extends_type, true_type, false_type);
845 }
846 }
847 }
848 Type::Interface(type_str.to_string())
850 }
851 else if type_str.starts_with("keyof ") {
852 let target_type_str = type_str.trim_start_matches("keyof ").trim();
854 let target_type = self.parse_type(target_type_str);
855 self.handle_keyof_type(target_type)
856 }
857 else if type_str.contains("[") && type_str.contains("]") {
858 let parts: Vec<&str> = type_str.split("[").collect();
860 if parts.len() == 2 {
861 let object_type_str = parts[0].trim();
862 let index_type_str = parts[1].trim_end_matches("]").trim();
863
864 let object_type = self.parse_type(object_type_str);
865 let index_type = self.parse_type(index_type_str);
866 return self.handle_index_access_type(object_type, index_type);
867 }
868 Type::Interface(type_str.to_string())
870 }
871 else if (type_str.starts_with('"') && type_str.ends_with('"')) || (type_str.starts_with('\'') && type_str.ends_with('\'')) {
872 let literal = type_str[1..type_str.len() - 1].to_string();
874 self.handle_literal_type(literal)
875 }
876 else if type_str.chars().all(|c| c.is_digit(10)) {
877 self.handle_literal_type(type_str.to_string())
879 }
880 else if type_str == "true" || type_str == "false" {
881 self.handle_literal_type(type_str.to_string())
883 }
884 else {
885 Type::Interface(type_str.to_string())
887 }
888 }
889 }
890 }
891}
892
893impl TypeChecker {
895 fn parse_generic_type(&self, type_str: &str) -> Option<(String, Vec<Type>)> {
905 let mut bracket_count = 0;
907 let mut start_index = None;
908 let mut end_index = None;
909
910 for (i, c) in type_str.chars().enumerate() {
911 if c == '<' {
912 if start_index.is_none() {
913 start_index = Some(i);
914 }
915 bracket_count += 1;
916 }
917 else if c == '>' {
918 bracket_count -= 1;
919 if bracket_count == 0 {
920 end_index = Some(i);
921 break;
922 }
923 }
924 }
925
926 if let (Some(start), Some(end)) = (start_index, end_index) {
927 let name = type_str[..start].trim().to_string();
928 let type_args_str = &type_str[start + 1..end].trim();
929
930 let type_args = self.parse_type_args(type_args_str);
932 Some((name, type_args))
933 }
934 else {
935 None
936 }
937 }
938
939 fn parse_type_args(&self, type_args_str: &str) -> Vec<Type> {
949 let mut type_args = Vec::new();
950 let mut current_arg = String::new();
951 let mut bracket_count = 0;
952
953 for c in type_args_str.chars() {
954 if c == ',' && bracket_count == 0 {
955 if !current_arg.trim().is_empty() {
957 type_args.push(self.parse_type(¤t_arg));
958 current_arg.clear();
959 }
960 }
961 else {
962 if c == '<' {
964 bracket_count += 1;
965 }
966 else if c == '>' {
967 bracket_count -= 1;
968 }
969 current_arg.push(c);
970 }
971 }
972
973 if !current_arg.trim().is_empty() {
975 type_args.push(self.parse_type(¤t_arg));
976 }
977
978 type_args
979 }
980
981 fn handle_partial_type(&self, type_args: Vec<Type>) -> Type {
983 if let Some(target_type) = type_args.get(0) {
984 let key_var = "K".to_string();
986 let source_type = self.handle_keyof_type(target_type.clone());
987 let mapped_type = Type::Union(vec![target_type.clone(), Type::Void]);
988 self.handle_mapped_type(key_var, source_type, mapped_type)
989 }
990 else {
991 Type::Any
992 }
993 }
994
995 fn handle_required_type(&self, type_args: Vec<Type>) -> Type {
997 if let Some(target_type) = type_args.get(0) {
998 let key_var = "K".to_string();
1000 let source_type = self.handle_keyof_type(target_type.clone());
1001 let mapped_type = target_type.clone();
1002 self.handle_mapped_type(key_var, source_type, mapped_type)
1003 }
1004 else {
1005 Type::Any
1006 }
1007 }
1008
1009 fn handle_pick_type(&self, type_args: Vec<Type>) -> Type {
1011 if type_args.len() >= 2 {
1012 let target_type = &type_args[0];
1013 let keys_type = &type_args[1];
1014 let key_var = "K".to_string();
1016 let source_type = Type::Intersection(vec![self.handle_keyof_type(target_type.clone()), keys_type.clone()]);
1017 let mapped_type = self.handle_index_access_type(target_type.clone(), Type::TypeVar(key_var.clone()));
1018 self.handle_mapped_type(key_var, source_type, mapped_type)
1019 }
1020 else {
1021 Type::Any
1022 }
1023 }
1024
1025 fn handle_omit_type(&self, type_args: Vec<Type>) -> Type {
1027 if type_args.len() >= 2 {
1028 let target_type = &type_args[0];
1029 let keys_type = &type_args[1];
1030 let key_var = "K".to_string();
1032 let source_type = Type::Exclude(Box::new(self.handle_keyof_type(target_type.clone())), Box::new(keys_type.clone()));
1033 let mapped_type = self.handle_index_access_type(target_type.clone(), Type::TypeVar(key_var.clone()));
1034 self.handle_mapped_type(key_var, source_type, mapped_type)
1035 }
1036 else {
1037 Type::Any
1038 }
1039 }
1040
1041 fn handle_record_type(&self, type_args: Vec<Type>) -> Type {
1043 if type_args.len() >= 2 {
1044 let key_type = &type_args[0];
1045 let value_type = &type_args[1];
1046 let key_var = "K".to_string();
1048 let source_type = key_type.clone();
1049 let mapped_type = value_type.clone();
1050 self.handle_mapped_type(key_var, source_type, mapped_type)
1051 }
1052 else {
1053 Type::Any
1054 }
1055 }
1056
1057 fn handle_exclude_type(&self, type_args: Vec<Type>) -> Type {
1059 if type_args.len() >= 2 {
1060 let target_type = &type_args[0];
1061 let exclude_type = &type_args[1];
1062 Type::Exclude(Box::new(target_type.clone()), Box::new(exclude_type.clone()))
1064 }
1065 else {
1066 Type::Any
1067 }
1068 }
1069
1070 fn handle_extract_type(&self, type_args: Vec<Type>) -> Type {
1072 if type_args.len() >= 2 {
1073 let target_type = &type_args[0];
1074 let extract_type = &type_args[1];
1075 Type::Extract(Box::new(target_type.clone()), Box::new(extract_type.clone()))
1077 }
1078 else {
1079 Type::Any
1080 }
1081 }
1082
1083 fn handle_non_nullable_type(&self, type_args: Vec<Type>) -> Type {
1085 if let Some(target_type) = type_args.get(0) {
1086 Type::Exclude(Box::new(target_type.clone()), Box::new(Type::Union(vec![Type::Void, Type::Void])))
1088 }
1089 else {
1090 Type::Any
1091 }
1092 }
1093
1094 fn handle_this_type(&self, type_args: Vec<Type>) -> Type {
1096 if let Some(this_type) = type_args.get(0) {
1097 Type::Any
1100 }
1101 else {
1102 Type::Any
1103 }
1104 }
1105
1106 fn handle_omit_this_parameter_type(&self, type_args: Vec<Type>) -> Type {
1108 if let Some(target_type) = type_args.get(0) {
1109 match target_type {
1110 Type::Function(params, return_type) => {
1111 let new_params = if !params.is_empty() { params[1..].to_vec() } else { params.clone() };
1113 Type::Function(new_params, return_type.clone())
1114 }
1115 _ => Type::Any,
1116 }
1117 }
1118 else {
1119 Type::Any
1120 }
1121 }
1122
1123 fn handle_this_parameter_type(&self, type_args: Vec<Type>) -> Type {
1125 if let Some(target_type) = type_args.get(0) {
1126 match target_type {
1127 Type::Function(params, _) => {
1128 if !params.is_empty() {
1130 params[0].clone()
1131 }
1132 else {
1133 Type::Any
1134 }
1135 }
1136 _ => Type::Any,
1137 }
1138 }
1139 else {
1140 Type::Any
1141 }
1142 }
1143
1144 fn handle_parameters_type(&self, type_args: Vec<Type>) -> Type {
1146 if let Some(target_type) = type_args.get(0) {
1147 match target_type {
1148 Type::Function(params, _) => {
1149 Type::Tuple(params.clone())
1151 }
1152 _ => Type::Any,
1153 }
1154 }
1155 else {
1156 Type::Any
1157 }
1158 }
1159
1160 fn handle_return_type(&self, type_args: Vec<Type>) -> Type {
1162 if let Some(target_type) = type_args.get(0) {
1163 match target_type {
1164 Type::Function(_, return_type) => {
1165 *return_type.clone()
1167 }
1168 _ => Type::Any,
1169 }
1170 }
1171 else {
1172 Type::Any
1173 }
1174 }
1175
1176 fn handle_instance_type(&self, type_args: Vec<Type>) -> Type {
1178 if let Some(target_type) = type_args.get(0) {
1179 Type::Any
1182 }
1183 else {
1184 Type::Any
1185 }
1186 }
1187
1188 fn collect_file_dependencies(&self, file_path: &str) -> Vec<String> {
1190 Vec::new()
1193 }
1194
1195 fn update_file_dependencies(&mut self, file_path: &str, dependencies: &[String]) {
1197 self.file_dependencies.insert(file_path.to_string(), dependencies.to_vec());
1199
1200 for dep in dependencies {
1202 if let Some(dependents) = self.file_dependencies.get_mut(dep) {
1203 if !dependents.contains(&file_path.to_string()) {
1204 dependents.push(file_path.to_string());
1205 }
1206 }
1207 else {
1208 self.file_dependencies.insert(dep.to_string(), vec![file_path.to_string()]);
1209 }
1210 }
1211 }
1212
1213 fn get_file_dependents(&self, file_path: &str) -> Vec<String> {
1215 let mut dependents = Vec::new();
1216
1217 for (file, deps) in &self.file_dependencies {
1219 if deps.contains(&file_path.to_string()) {
1220 dependents.push(file.clone());
1221 }
1222 }
1223
1224 dependents
1225 }
1226
1227 pub async fn incremental_check(&mut self, files: Vec<PathBuf>) -> NargoResult<TypeCheckResult> {
1237 let mut errors = 0;
1238 let mut checked_files = 0;
1239
1240 let mut files_to_check = HashSet::new();
1242 for file in files {
1243 let file_path_str = file.to_string_lossy().to_string();
1244 files_to_check.insert(file_path_str.clone());
1245
1246 let dependents = self.get_file_dependents(&file_path_str);
1248 for dependent in dependents {
1249 files_to_check.insert(dependent);
1250 }
1251 }
1252
1253 for file_path_str in files_to_check {
1255 let file = PathBuf::from(file_path_str);
1256 if file.exists() {
1257 let source = std::fs::read_to_string(&file).map_err(Error::io_error)?;
1258 match self.check_file(&file, &source) {
1259 Ok(_) => {}
1260 Err(e) => {
1261 eprintln!("{} {}: {}", console::style("✘").red(), file.display(), e);
1262 errors += 1;
1263 }
1264 }
1265 checked_files += 1;
1266 }
1267 }
1268
1269 Ok(TypeCheckResult { errors, checked_files })
1270 }
1271}
1272
1273impl ExpressionChecker for TypeChecker {
1275 fn check_program(&mut self, program: &nargo_ir::JsProgram) {
1276 for stmt in &program.body {
1277 self.check_statement(stmt);
1278 }
1279 }
1280
1281 fn check_statement(&mut self, stmt: &nargo_ir::JsStmt) {
1282 match stmt {
1283 nargo_ir::JsStmt::VariableDecl { id, init, .. } => {
1284 let mut var_type = Type::Any;
1286
1287 if let Some(init_expr) = init {
1289 let init_type = self.check_expression(init_expr);
1290 var_type = init_type;
1292 }
1293
1294 self.type_env.add_variable(id.clone(), var_type);
1296 }
1297 nargo_ir::JsStmt::FunctionDecl { id, params, body, .. } => {
1298 let mut param_types = Vec::new();
1300 for param in params {
1301 param_types.push(Type::Any);
1304 }
1305
1306 let return_type = Type::Any;
1309
1310 let func_type = Type::Function(param_types, Box::new(return_type));
1312
1313 self.type_env.add_function(id.clone(), func_type);
1315
1316 for stmt in body {
1318 self.check_statement(stmt);
1319 }
1320 }
1321 nargo_ir::JsStmt::Return(expr, _, _) => {
1322 if let Some(expr) = expr {
1323 self.check_expression(expr);
1324 }
1325 }
1326 nargo_ir::JsStmt::If { test, consequent, alternate, .. } => {
1327 self.check_expression(test);
1328 self.check_statement(consequent);
1329 if let Some(alt) = alternate {
1330 self.check_statement(alt);
1331 }
1332 }
1333 nargo_ir::JsStmt::While { test, body, .. } => {
1334 self.check_expression(test);
1335 self.check_statement(body);
1336 }
1337 nargo_ir::JsStmt::For { init, test, update, body, .. } => {
1338 if let Some(init_stmt) = init {
1339 self.check_statement(init_stmt);
1340 }
1341 if let Some(test_expr) = test {
1342 self.check_expression(test_expr);
1343 }
1344 if let Some(update_expr) = update {
1345 self.check_expression(update_expr);
1346 }
1347 self.check_statement(body);
1348 }
1349 nargo_ir::JsStmt::Block(stmts, _, _) => {
1350 for stmt in stmts {
1351 self.check_statement(stmt);
1352 }
1353 }
1354 nargo_ir::JsStmt::Expr(expr, _, _) => {
1355 self.check_expression(expr);
1356 }
1357 nargo_ir::JsStmt::Other(stmt_str, _, _) => {
1358 self.handle_typescript_stmt(stmt_str);
1360 }
1361 _ => {
1362 }
1364 }
1365 }
1366
1367 fn get_error_position(&self, expr: &nargo_ir::JsExpr) -> (usize, usize) {
1368 let span = expr.span();
1369 (span.start.offset as usize, span.end.offset as usize)
1370 }
1371
1372 fn get_error_location(&self, expr: &nargo_ir::JsExpr) -> (Option<usize>, Option<usize>) {
1373 let span = expr.span();
1374 (Some(span.start.line as usize), Some(span.start.column as usize))
1375 }
1376
1377 fn is_type_compatible(&self, actual: &Type, expected: &Type) -> bool {
1378 if actual == expected {
1380 return true;
1381 }
1382
1383 if *actual == Type::Any || *expected == Type::Any {
1385 return true;
1386 }
1387
1388 if *expected == Type::Unknown {
1390 return true;
1391 }
1392
1393 if *actual == Type::Never {
1395 return true;
1396 }
1397
1398 if let Type::Union(types) = actual {
1400 return types.iter().all(|t| self.is_type_compatible(t, expected));
1401 }
1402
1403 if let Type::Union(types) = expected {
1405 return types.iter().any(|t| self.is_type_compatible(actual, t));
1406 }
1407
1408 if let Type::Intersection(types) = expected {
1410 return types.iter().all(|t| self.is_type_compatible(actual, t));
1411 }
1412
1413 if let (Type::Array(actual_elem), Type::Array(expected_elem)) = (actual, expected) {
1415 return self.is_type_compatible(actual_elem, expected_elem);
1416 }
1417
1418 if let (Type::Function(actual_params, actual_return), Type::Function(expected_params, expected_return)) = (actual, expected) {
1420 if actual_params.len() > expected_params.len() {
1422 return false;
1423 }
1424 for (actual_param, expected_param) in actual_params.iter().zip(expected_params.iter()) {
1426 if !self.is_type_compatible(expected_param, actual_param) {
1427 return false;
1428 }
1429 }
1430 return self.is_type_compatible(actual_return, expected_return);
1432 }
1433
1434 if let (Type::Object(actual_members), Type::Object(expected_members)) = (actual, expected) {
1436 for (name, expected_type) in expected_members {
1438 if let Some(actual_type) = actual_members.get(name) {
1439 if !self.is_type_compatible(actual_type, expected_type) {
1440 return false;
1441 }
1442 }
1443 else {
1444 return false;
1445 }
1446 }
1447 return true;
1448 }
1449
1450 if let (Type::Interface(actual_name), Type::Interface(expected_name)) = (actual, expected) {
1452 return actual_name == expected_name;
1453 }
1454
1455 if let (Type::GenericInterface(actual_name, actual_args), Type::GenericInterface(expected_name, expected_args)) = (actual, expected) {
1457 if actual_name != expected_name || actual_args.len() != expected_args.len() {
1458 return false;
1459 }
1460 for (actual_arg, expected_arg) in actual_args.iter().zip(expected_args.iter()) {
1461 if !self.is_type_compatible(actual_arg, expected_arg) {
1462 return false;
1463 }
1464 }
1465 return true;
1466 }
1467
1468 if let (Type::TypeAlias(actual_name), Type::TypeAlias(expected_name)) = (actual, expected) {
1470 return actual_name == expected_name;
1471 }
1472
1473 if let (Type::GenericTypeAlias(actual_name, actual_args), Type::GenericTypeAlias(expected_name, expected_args)) = (actual, expected) {
1475 if actual_name != expected_name || actual_args.len() != expected_args.len() {
1476 return false;
1477 }
1478 for (actual_arg, expected_arg) in actual_args.iter().zip(expected_args.iter()) {
1479 if !self.is_type_compatible(actual_arg, expected_arg) {
1480 return false;
1481 }
1482 }
1483 return true;
1484 }
1485
1486 if let (Type::Tuple(actual_types), Type::Tuple(expected_types)) = (actual, expected) {
1488 if actual_types.len() != expected_types.len() {
1489 return false;
1490 }
1491 for (actual_type, expected_type) in actual_types.iter().zip(expected_types.iter()) {
1492 if !self.is_type_compatible(actual_type, expected_type) {
1493 return false;
1494 }
1495 }
1496 return true;
1497 }
1498
1499 if let (Type::Literal(actual_lit), Type::Literal(expected_lit)) = (actual, expected) {
1501 return actual_lit == expected_lit;
1502 }
1503
1504 if let Type::Literal(_) = actual {
1506 match expected {
1507 Type::String => return true,
1508 Type::Number => return true,
1509 Type::Boolean => return true,
1510 _ => return false,
1511 }
1512 }
1513
1514 false
1515 }
1516
1517 fn check_expression(&mut self, expr: &nargo_ir::JsExpr) -> Type {
1518 match expr {
1519 nargo_ir::JsExpr::Identifier(name, ..) => {
1520 if let Some(ty) = self.type_env.get_variable(name) {
1522 ty.clone()
1523 }
1524 else if let Some(ty) = self.type_env.get_function(name) {
1525 ty.clone()
1526 }
1527 else if let Some(ty) = self.type_env.get_type_alias(name) {
1528 ty.clone()
1529 }
1530 else {
1531 let (line, column) = self.get_error_location(expr);
1533 self.errors.push(TypeError { position: self.get_error_position(expr), message: format!("未定义的变量: {}", name), file_name: self.current_file.clone(), line, column, error_code: Some("TS2304".to_string()), related_types: None, suggestion: Some(format!("请确保变量 {} 已声明", name)) });
1534 Type::Any
1535 }
1536 }
1537 nargo_ir::JsExpr::Other(code, ..) => {
1538 Type::Any
1541 }
1542 nargo_ir::JsExpr::Literal(value, ..) => {
1543 match value {
1545 nargo_types::NargoValue::Number(_) => Type::Number,
1546 nargo_types::NargoValue::String(_) => Type::String,
1547 nargo_types::NargoValue::Bool(_) => Type::Boolean,
1548 nargo_types::NargoValue::Null => Type::Void,
1549 _ => Type::Any,
1550 }
1551 }
1552 nargo_ir::JsExpr::Binary { left, right, op, .. } => {
1553 let left_type = self.check_expression(left);
1554 let right_type = self.check_expression(right);
1555
1556 match op.as_str() {
1558 "+" | "-" | "*" | "/" | "%" => {
1559 if !self.is_type_compatible(&left_type, &Type::Number) || !self.is_type_compatible(&right_type, &Type::Number) {
1561 let (line, column) = self.get_error_location(expr);
1562 self.errors.push(TypeError { position: self.get_error_position(expr), message: "算术运算符要求操作数为数字类型".to_string(), file_name: self.current_file.clone(), line, column, error_code: Some("TS2365".to_string()), related_types: Some(vec!["number".to_string()]), suggestion: Some("请确保操作数为数字类型".to_string()) });
1563 }
1564 Type::Number
1565 }
1566 "==" | "!=" | "<" | "<=" | ">" | ">=" => {
1567 Type::Boolean
1569 }
1570 "&&" | "||" => {
1571 if !self.is_type_compatible(&left_type, &Type::Boolean) || !self.is_type_compatible(&right_type, &Type::Boolean) {
1573 let (line, column) = self.get_error_location(expr);
1574 self.errors.push(TypeError { position: self.get_error_position(expr), message: "逻辑运算符要求操作数为布尔类型".to_string(), file_name: self.current_file.clone(), line, column, error_code: Some("TS2365".to_string()), related_types: Some(vec!["boolean".to_string()]), suggestion: Some("请确保操作数为布尔类型".to_string()) });
1575 }
1576 Type::Boolean
1577 }
1578 "in" => {
1579 Type::Boolean
1582 }
1583 "instanceof" => {
1584 Type::Boolean
1586 }
1587 _ => Type::Any,
1588 }
1589 }
1590 nargo_ir::JsExpr::Unary { op, argument, .. } => {
1591 let arg_type = self.check_expression(argument);
1592
1593 match op.as_str() {
1594 "!" => {
1595 if !self.is_type_compatible(&arg_type, &Type::Boolean) {
1597 let (line, column) = self.get_error_location(expr);
1598 self.errors.push(TypeError { position: self.get_error_position(expr), message: "逻辑非运算符要求操作数为布尔类型".to_string(), file_name: self.current_file.clone(), line, column, error_code: Some("TS2365".to_string()), related_types: Some(vec!["boolean".to_string()]), suggestion: Some("请确保操作数为布尔类型".to_string()) });
1599 }
1600 Type::Boolean
1601 }
1602 "-" => {
1603 if !self.is_type_compatible(&arg_type, &Type::Number) {
1605 let (line, column) = self.get_error_location(expr);
1606 self.errors.push(TypeError { position: self.get_error_position(expr), message: "一元负号运算符要求操作数为数字类型".to_string(), file_name: self.current_file.clone(), line, column, error_code: Some("TS2365".to_string()), related_types: Some(vec!["number".to_string()]), suggestion: Some("请确保操作数为数字类型".to_string()) });
1607 }
1608 Type::Number
1609 }
1610 "typeof" => {
1611 Type::String
1613 }
1614 _ => Type::Any,
1615 }
1616 }
1617 nargo_ir::JsExpr::Call { callee, args, .. } => {
1618 let callee_type = self.check_expression(callee);
1620
1621 match callee_type {
1622 Type::Function(param_types, return_type) => {
1623 if args.len() != param_types.len() {
1625 let (line, column) = self.get_error_location(expr);
1626 self.errors.push(TypeError { position: self.get_error_position(expr), message: format!("函数调用参数数量不匹配: 期望 {} 个参数,实际提供 {} 个参数", param_types.len(), args.len()), file_name: self.current_file.clone(), line, column, error_code: Some("TS2554".to_string()), related_types: None, suggestion: Some("请提供正确数量的参数".to_string()) });
1627 }
1628
1629 for (i, (arg, expected_type)) in args.iter().zip(param_types.iter()).enumerate() {
1631 let arg_type = self.check_expression(arg);
1632 if !self.is_type_compatible(&arg_type, expected_type) {
1633 let (line, column) = self.get_error_location(expr);
1634 self.errors.push(TypeError { position: self.get_error_position(expr), message: format!("第 {} 个参数类型不匹配: 期望 {:?},实际为 {:?}", i + 1, expected_type, arg_type), file_name: self.current_file.clone(), line, column, error_code: Some("TS2345".to_string()), related_types: Some(vec![format!("{:?}", expected_type), format!("{:?}", arg_type)]), suggestion: Some("请确保参数类型与函数声明匹配".to_string()) });
1635 }
1636 }
1637
1638 *return_type
1639 }
1640 _ => {
1641 let (line, column) = self.get_error_location(expr);
1643 self.errors.push(TypeError { position: self.get_error_position(expr), message: "非函数类型不能被调用".to_string(), file_name: self.current_file.clone(), line, column, error_code: Some("TS2349".to_string()), related_types: None, suggestion: Some("请确保调用的是函数类型".to_string()) });
1644 Type::Any
1645 }
1646 }
1647 }
1648 nargo_ir::JsExpr::Member { object, property, computed, .. } => {
1649 let obj_type = self.check_expression(object);
1651
1652 match obj_type {
1653 Type::Object(members) => {
1654 if *computed {
1655 Type::Any
1657 }
1658 else {
1659 if let nargo_ir::JsExpr::Identifier(prop_name, _, _) = &**property {
1661 if let Some(prop_type) = members.get(prop_name) {
1662 prop_type.clone()
1663 }
1664 else {
1665 let (line, column) = self.get_error_location(expr);
1667 self.errors.push(TypeError { position: self.get_error_position(expr), message: format!("对象不存在属性: {}", prop_name), file_name: self.current_file.clone(), line, column, error_code: Some("TS2339".to_string()), related_types: None, suggestion: Some(format!("请确保对象具有属性 {}", prop_name)) });
1668 Type::Any
1669 }
1670 }
1671 else {
1672 Type::Any
1673 }
1674 }
1675 }
1676 Type::Array(_) => {
1677 if let Type::Array(element_type) = obj_type {
1679 *element_type
1680 }
1681 else {
1682 Type::Any
1683 }
1684 }
1685 Type::Interface(interface_name) => {
1686 if let Some(interface) = self.type_env.get_interface(&interface_name) {
1688 if !*computed {
1689 if let nargo_ir::JsExpr::Identifier(prop_name, _, _) = &**property {
1690 if let Some(prop_type) = interface.members.get(prop_name) {
1691 prop_type.clone()
1692 }
1693 else {
1694 let (line, column) = self.get_error_location(expr);
1696 self.errors.push(TypeError { position: self.get_error_position(expr), message: format!("接口 {} 不存在属性: {}", interface_name, prop_name), file_name: self.current_file.clone(), line, column, error_code: Some("TS2339".to_string()), related_types: None, suggestion: Some(format!("请确保接口 {} 具有属性 {}", interface_name, prop_name)) });
1697 Type::Any
1698 }
1699 }
1700 else {
1701 Type::Any
1702 }
1703 }
1704 else {
1705 Type::Any
1706 }
1707 }
1708 else {
1709 let (line, column) = self.get_error_location(expr);
1711 self.errors.push(TypeError { position: self.get_error_position(expr), message: format!("未定义的接口: {}", interface_name), file_name: self.current_file.clone(), line, column, error_code: Some("TS2304".to_string()), related_types: None, suggestion: Some(format!("请确保接口 {} 已定义", interface_name)) });
1712 Type::Any
1713 }
1714 }
1715 _ => {
1716 let (line, column) = self.get_error_location(expr);
1718 self.errors.push(TypeError { position: self.get_error_position(expr), message: "非对象或数组类型不能访问成员".to_string(), file_name: self.current_file.clone(), line, column, error_code: Some("TS2339".to_string()), related_types: None, suggestion: Some("请确保访问成员的是对象或数组类型".to_string()) });
1719 Type::Any
1720 }
1721 }
1722 }
1723 nargo_ir::JsExpr::Array(elements, _, _) => {
1724 if elements.is_empty() {
1726 Type::Array(Box::new(Type::Any))
1727 }
1728 else {
1729 let element_type = self.check_expression(&elements[0]);
1731 Type::Array(Box::new(element_type))
1732 }
1733 }
1734 nargo_ir::JsExpr::Object(properties, _, _) => {
1735 let mut obj_type = HashMap::new();
1737 for (key, value) in properties {
1738 let value_type = self.check_expression(value);
1739 obj_type.insert(key.clone(), value_type);
1740 }
1741 Type::Object(obj_type)
1742 }
1743 nargo_ir::JsExpr::ArrowFunction { params, body, .. } => {
1744 let mut param_types = Vec::new();
1746 for param in params {
1747 param_types.push(Type::Any);
1749 }
1750
1751 let return_type = self.check_expression(body);
1753
1754 Type::Function(param_types, Box::new(return_type))
1755 }
1756 nargo_ir::JsExpr::Conditional { test, consequent, alternate, .. } => {
1757 let test_type = self.check_expression(test);
1759 if test_type != Type::Boolean {
1760 self.errors.push(TypeError {
1761 position: (0, 0), message: "条件表达式要求测试表达式为布尔类型".to_string(),
1763 file_name: None,
1764 line: None,
1765 column: None,
1766 error_code: Some("TS2365".to_string()),
1767 related_types: Some(vec!["boolean".to_string()]),
1768 suggestion: Some("请确保测试表达式为布尔类型".to_string()),
1769 });
1770 }
1771
1772 let consequent_type = self.check_expression(consequent);
1774 let alternate_type = self.check_expression(alternate);
1775
1776 Type::Any
1778 }
1779 nargo_ir::JsExpr::TemplateLiteral { expressions, .. } => {
1780 for expr in expressions {
1782 self.check_expression(expr);
1783 }
1784 Type::String
1785 }
1786 nargo_ir::JsExpr::TseElement { tag, attributes, children, .. } => {
1787 for attr in attributes {
1790 if let Some(value) = &attr.value {
1791 self.check_expression(value);
1792 }
1793 }
1794 for child in children {
1795 self.check_expression(child);
1796 }
1797 Type::Any
1798 }
1799 nargo_ir::JsExpr::Spread(expr, ..) => {
1800 let expr_type = self.check_expression(expr);
1802 expr_type
1803 }
1804 _ => {
1805 Type::Any
1807 }
1808 }
1809 }
1810
1811 fn check_template(&mut self, template: &nargo_ir::TemplateIR) {
1812 self.check_template_impl(template);
1813 }
1814
1815 fn check_template_node(&mut self, node: &nargo_ir::TemplateNodeIR) {
1816 self.check_template_node_impl(node);
1817 }
1818
1819 fn check_element(&mut self, element: &nargo_ir::ElementIR) {
1820 self.check_element_impl(element);
1821 }
1822
1823 fn check_attribute(&mut self, attr: &nargo_ir::AttributeIR) {
1824 self.check_attribute_impl(attr);
1825 }
1826
1827 fn check_if_node(&mut self, if_node: &nargo_ir::IfNodeIR) {
1828 self.check_if_node_impl(if_node);
1829 }
1830
1831 fn check_for_node(&mut self, for_node: &nargo_ir::ForNodeIR) {
1832 self.check_for_node_impl(for_node);
1833 }
1834
1835 fn check_interpolation(&mut self, interpolation: &nargo_ir::ExpressionIR) {
1836 self.check_interpolation_impl(interpolation);
1837 }
1838}
1839
1840impl TemplateChecker for TypeChecker {
1842 fn check_template(&mut self, template: &nargo_ir::TemplateIR) {
1843 self.check_template_impl(template);
1844 }
1845
1846 fn check_template_node(&mut self, node: &nargo_ir::TemplateNodeIR) {
1847 self.check_template_node_impl(node);
1848 }
1849
1850 fn check_element(&mut self, element: &nargo_ir::ElementIR) {
1851 self.check_element_impl(element);
1852 }
1853
1854 fn check_attribute(&mut self, attr: &nargo_ir::AttributeIR) {
1855 self.check_attribute_impl(attr);
1856 }
1857
1858 fn check_if_node(&mut self, if_node: &nargo_ir::IfNodeIR) {
1859 self.check_if_node_impl(if_node);
1860 }
1861
1862 fn check_for_node(&mut self, for_node: &nargo_ir::ForNodeIR) {
1863 self.check_for_node_impl(for_node);
1864 }
1865
1866 fn check_interpolation(&mut self, interpolation: &nargo_ir::ExpressionIR) {
1867 self.check_interpolation_impl(interpolation);
1868 }
1869}
1870
1871impl Utils for TypeChecker {
1873 fn find_frontend_files(&self, dir: &PathBuf) -> NargoResult<Vec<PathBuf>> {
1874 let mut files = Vec::new();
1875
1876 if dir.is_file() {
1877 files.push(dir.clone());
1878 }
1879 else if dir.is_dir() {
1880 if let Some(config) = &self.config {
1882 if let Some(include) = &config.include {
1884 for pattern in include {
1885 let expanded_patterns = self.expand_glob_pattern(dir, pattern);
1886 for pattern in expanded_patterns {
1887 if let Ok(matches) = glob::glob(&pattern) {
1888 for entry in matches {
1889 if let Ok(path) = entry {
1890 if path.is_file() {
1891 files.push(path);
1892 }
1893 }
1894 }
1895 }
1896 }
1897 }
1898 }
1899 else {
1900 self.find_files_recursive(dir, &mut files);
1902 }
1903 }
1904 else {
1905 self.find_files_recursive(dir, &mut files);
1907 }
1908 }
1909
1910 Ok(files)
1911 }
1912
1913 fn find_files_recursive(&self, dir: &PathBuf, files: &mut Vec<PathBuf>) {
1914 if let Ok(entries) = std::fs::read_dir(dir) {
1915 for entry in entries {
1916 if let Ok(entry) = entry {
1917 let path = entry.path();
1918 if path.is_dir() {
1919 if !self.should_exclude(&path) {
1921 self.find_files_recursive(&path, files);
1922 }
1923 }
1924 else if let Some(ext) = path.extension() {
1925 if ext == "ts" || ext == "tsx" || ext == "js" || ext == "jsx" {
1926 files.push(path);
1927 }
1928 }
1929 }
1930 }
1931 }
1932 }
1933
1934 fn should_exclude(&self, path: &PathBuf) -> bool {
1935 if let Some(config) = &self.config {
1936 if let Some(exclude) = &config.exclude {
1937 let path_str = path.to_string_lossy();
1938 for pattern in exclude {
1939 if self.matches_pattern(&path_str, pattern) {
1940 return true;
1941 }
1942 }
1943 }
1944 }
1945 let file_name = path.file_name().unwrap_or_default().to_string_lossy();
1947 file_name == "node_modules" || file_name == ".git"
1948 }
1949
1950 fn expand_glob_pattern(&self, base_dir: &PathBuf, pattern: &str) -> Vec<String> {
1951 let base_path = base_dir.to_string_lossy();
1952 let pattern = pattern.replace("**/*", "*");
1953 vec![format!("{}/{}", base_path, pattern)]
1954 }
1955
1956 fn matches_pattern(&self, path: &str, pattern: &str) -> bool {
1957 let pattern = pattern.replace("**", ".*");
1959 let pattern = pattern.replace("*", ".*");
1960 if let Ok(re) = Regex::new(&format!("^{}$", pattern)) {
1961 re.is_match(path)
1962 }
1963 else {
1964 false
1965 }
1966 }
1967}
1968
1969impl TypeChecker {
1971 fn check_template_impl(&mut self, template: &nargo_ir::TemplateIR) {
1973 for node in &template.nodes {
1974 self.check_template_node_impl(node);
1975 }
1976 }
1977
1978 fn check_template_node_impl(&mut self, node: &nargo_ir::TemplateNodeIR) {
1980 match node {
1981 nargo_ir::TemplateNodeIR::Element(element) => {
1982 self.check_element_impl(element);
1983 }
1984 nargo_ir::TemplateNodeIR::If(if_node) => {
1985 self.check_if_node_impl(if_node);
1986 }
1987 nargo_ir::TemplateNodeIR::For(for_node) => {
1988 self.check_for_node_impl(for_node);
1989 }
1990 nargo_ir::TemplateNodeIR::Interpolation(interpolation) => {
1991 self.check_interpolation_impl(interpolation);
1992 }
1993 _ => {
1994 }
1996 }
1997 }
1998
1999 fn check_element_impl(&mut self, element: &nargo_ir::ElementIR) {
2001 for attr in &element.attributes {
2002 self.check_attribute_impl(attr);
2003 }
2004 for child in &element.children {
2005 self.check_template_node_impl(child);
2006 }
2007 }
2008
2009 fn check_attribute_impl(&mut self, attr: &nargo_ir::AttributeIR) {
2011 if let Some(value_ast) = &attr.value_ast {
2012 self.check_expression(value_ast);
2013 }
2014 }
2015
2016 fn check_if_node_impl(&mut self, if_node: &nargo_ir::IfNodeIR) {
2018 if let Some(ast) = &if_node.condition.ast {
2019 self.check_expression(ast);
2020 }
2021 for child in &if_node.consequent {
2022 self.check_template_node_impl(child);
2023 }
2024 if let Some(alternate) = &if_node.alternate {
2025 for child in alternate {
2026 self.check_template_node_impl(child);
2027 }
2028 }
2029 for (condition, body) in &if_node.else_ifs {
2030 if let Some(ast) = &condition.ast {
2031 self.check_expression(ast);
2032 }
2033 for child in body {
2034 self.check_template_node_impl(child);
2035 }
2036 }
2037 }
2038
2039 fn check_for_node_impl(&mut self, for_node: &nargo_ir::ForNodeIR) {
2041 if let Some(ast) = &for_node.iterator.collection.ast {
2042 self.check_expression(ast);
2043 }
2044 for child in &for_node.body {
2045 self.check_template_node_impl(child);
2046 }
2047 }
2048
2049 fn check_interpolation_impl(&mut self, interpolation: &nargo_ir::ExpressionIR) {
2051 if let Some(ast) = &interpolation.ast {
2052 self.check_expression(ast);
2053 }
2054 }
2055}