Skip to main content

depyler_oracle/
patterns.rs

1//! Fix pattern templates with code transformations.
2//!
3//! Provides structured fix templates that can be applied to specific error patterns.
4//! Each template includes:
5//! - Pattern matching rules
6//! - Code transformation templates
7//! - Contextual hints
8
9use std::collections::HashMap;
10
11use serde::{Deserialize, Serialize};
12
13use crate::classifier::ErrorCategory;
14
15/// A code transformation template.
16#[derive(Clone, Debug, Serialize, Deserialize)]
17pub struct CodeTransform {
18    /// Description of the transformation
19    pub description: String,
20    /// Pattern to match (regex-like)
21    pub match_pattern: String,
22    /// Replacement template (with capture groups)
23    pub replacement: String,
24    /// Example before transformation
25    pub example_before: String,
26    /// Example after transformation
27    pub example_after: String,
28}
29
30impl CodeTransform {
31    /// Create a new code transformation.
32    #[must_use]
33    pub fn new(
34        description: &str,
35        match_pattern: &str,
36        replacement: &str,
37        example_before: &str,
38        example_after: &str,
39    ) -> Self {
40        Self {
41            description: description.to_string(),
42            match_pattern: match_pattern.to_string(),
43            replacement: replacement.to_string(),
44            example_before: example_before.to_string(),
45            example_after: example_after.to_string(),
46        }
47    }
48}
49
50/// A comprehensive fix template with multiple strategies.
51#[derive(Clone, Debug, Serialize, Deserialize)]
52pub struct FixTemplate {
53    /// Unique identifier for the template
54    pub id: String,
55    /// Human-readable name
56    pub name: String,
57    /// Error category this template addresses
58    pub category: ErrorCategory,
59    /// Keywords that trigger this template
60    pub trigger_keywords: Vec<String>,
61    /// Detailed explanation
62    pub explanation: String,
63    /// Code transformations (if applicable)
64    pub transforms: Vec<CodeTransform>,
65    /// General fix suggestions
66    pub suggestions: Vec<String>,
67    /// Links to documentation
68    pub doc_links: Vec<String>,
69    /// Priority (higher = more relevant)
70    pub priority: u32,
71}
72
73impl FixTemplate {
74    /// Create a new fix template builder.
75    #[must_use]
76    pub fn builder(id: &str, name: &str, category: ErrorCategory) -> FixTemplateBuilder {
77        FixTemplateBuilder::new(id, name, category)
78    }
79
80    /// Check if this template matches an error message.
81    #[must_use]
82    pub fn matches(&self, error_message: &str) -> bool {
83        let lower = error_message.to_lowercase();
84        self.trigger_keywords
85            .iter()
86            .any(|kw| lower.contains(&kw.to_lowercase()))
87    }
88
89    /// Calculate match score for an error message.
90    #[must_use]
91    pub fn match_score(&self, error_message: &str) -> f32 {
92        let lower = error_message.to_lowercase();
93        let matched = self
94            .trigger_keywords
95            .iter()
96            .filter(|kw| lower.contains(&kw.to_lowercase()))
97            .count();
98
99        if self.trigger_keywords.is_empty() {
100            return 0.0;
101        }
102
103        let keyword_score = matched as f32 / self.trigger_keywords.len() as f32;
104        keyword_score * (self.priority as f32 / 100.0)
105    }
106}
107
108/// Builder for `FixTemplate`.
109pub struct FixTemplateBuilder {
110    template: FixTemplate,
111}
112
113impl FixTemplateBuilder {
114    fn new(id: &str, name: &str, category: ErrorCategory) -> Self {
115        Self {
116            template: FixTemplate {
117                id: id.to_string(),
118                name: name.to_string(),
119                category,
120                trigger_keywords: Vec::new(),
121                explanation: String::new(),
122                transforms: Vec::new(),
123                suggestions: Vec::new(),
124                doc_links: Vec::new(),
125                priority: 50,
126            },
127        }
128    }
129
130    /// Add trigger keywords.
131    #[must_use]
132    pub fn with_keywords(mut self, keywords: &[&str]) -> Self {
133        self.template.trigger_keywords = keywords.iter().map(|s| (*s).to_string()).collect();
134        self
135    }
136
137    /// Set explanation.
138    #[must_use]
139    pub fn with_explanation(mut self, explanation: &str) -> Self {
140        self.template.explanation = explanation.to_string();
141        self
142    }
143
144    /// Add a code transformation.
145    #[must_use]
146    pub fn with_transform(mut self, transform: CodeTransform) -> Self {
147        self.template.transforms.push(transform);
148        self
149    }
150
151    /// Add suggestions.
152    #[must_use]
153    pub fn with_suggestions(mut self, suggestions: &[&str]) -> Self {
154        self.template.suggestions = suggestions.iter().map(|s| (*s).to_string()).collect();
155        self
156    }
157
158    /// Add documentation links.
159    #[must_use]
160    pub fn with_docs(mut self, links: &[&str]) -> Self {
161        self.template.doc_links = links.iter().map(|s| (*s).to_string()).collect();
162        self
163    }
164
165    /// Set priority.
166    #[must_use]
167    pub fn with_priority(mut self, priority: u32) -> Self {
168        self.template.priority = priority;
169        self
170    }
171
172    /// Build the template.
173    #[must_use]
174    pub fn build(self) -> FixTemplate {
175        self.template
176    }
177}
178
179/// Registry of fix templates.
180pub struct FixTemplateRegistry {
181    /// Templates indexed by category
182    templates: HashMap<ErrorCategory, Vec<FixTemplate>>,
183}
184
185impl FixTemplateRegistry {
186    /// Create a new empty registry.
187    #[must_use]
188    pub fn new() -> Self {
189        Self {
190            templates: HashMap::new(),
191        }
192    }
193
194    /// Create a registry with default Rust error templates.
195    #[must_use]
196    pub fn with_rust_defaults() -> Self {
197        let mut registry = Self::new();
198        register_type_mismatch_templates(&mut registry);
199        register_borrow_checker_templates(&mut registry);
200        register_lifetime_templates(&mut registry);
201        register_trait_bound_templates(&mut registry);
202        register_import_templates(&mut registry);
203        register_syntax_templates(&mut registry);
204        // DEPYLER-1309: Bootstrap top 50 transpiler-specific patterns
205        register_transpiler_patterns(&mut registry);
206        registry
207    }
208
209    /// Register a template.
210    pub fn register(&mut self, template: FixTemplate) {
211        self.templates
212            .entry(template.category)
213            .or_default()
214            .push(template);
215    }
216
217    /// Get templates for a category.
218    #[must_use]
219    pub fn get_templates(&self, category: ErrorCategory) -> &[FixTemplate] {
220        self.templates.get(&category).map_or(&[], |v| v.as_slice())
221    }
222
223    /// Find matching templates for an error message.
224    #[must_use]
225    pub fn find_matches(&self, error_message: &str) -> Vec<&FixTemplate> {
226        let mut matches: Vec<(&FixTemplate, f32)> = self
227            .templates
228            .values()
229            .flatten()
230            .filter_map(|t| {
231                let score = t.match_score(error_message);
232                if score > 0.0 {
233                    Some((t, score))
234                } else {
235                    None
236                }
237            })
238            .collect();
239
240        matches.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
241
242        matches.into_iter().map(|(t, _)| t).collect()
243    }
244
245    /// Find best matching template.
246    #[must_use]
247    pub fn find_best_match(&self, error_message: &str) -> Option<&FixTemplate> {
248        self.find_matches(error_message).into_iter().next()
249    }
250
251    /// Get all templates.
252    #[must_use]
253    pub fn all_templates(&self) -> Vec<&FixTemplate> {
254        self.templates.values().flatten().collect()
255    }
256
257    /// Total template count.
258    #[must_use]
259    pub fn template_count(&self) -> usize {
260        self.templates.values().map(|v| v.len()).sum()
261    }
262}
263
264impl Default for FixTemplateRegistry {
265    fn default() -> Self {
266        Self::with_rust_defaults()
267    }
268}
269
270// ============================================
271// Default Rust Error Templates
272// ============================================
273
274fn register_type_mismatch_templates(registry: &mut FixTemplateRegistry) {
275    // Integer type conversion
276    registry.register(
277        FixTemplate::builder(
278            "type-int-convert",
279            "Integer Type Conversion",
280            ErrorCategory::TypeMismatch,
281        )
282        .with_keywords(&[
283            "expected", "found", "i32", "i64", "u32", "u64", "isize", "usize",
284        ])
285        .with_explanation(
286            "Rust has strict type checking for integers. Different integer types \
287                 cannot be implicitly converted. Use explicit conversion with `as` or `.into()`.",
288        )
289        .with_transform(CodeTransform::new(
290            "Convert using `as`",
291            r"(\w+)",
292            "$1 as TARGET_TYPE",
293            "let x: i32 = some_u64;",
294            "let x: i32 = some_u64 as i32;",
295        ))
296        .with_suggestions(&[
297            "Use `as` for explicit numeric conversion",
298            "Consider using `.try_into()` for fallible conversion",
299            "Check if the source value fits in the target type",
300        ])
301        .with_docs(&["https://doc.rust-lang.org/std/convert/trait.Into.html"])
302        .with_priority(80)
303        .build(),
304    );
305
306    // String type conversion
307    registry.register(
308        FixTemplate::builder("type-string-convert", "String Type Conversion", ErrorCategory::TypeMismatch)
309            .with_keywords(&["expected", "found", "String", "&str", "string", "str"])
310            .with_explanation(
311                "Rust distinguishes between owned strings (String) and string slices (&str). \
312                 Use `.to_string()` to convert &str to String, or `&` / `.as_str()` for the reverse."
313            )
314            .with_transform(CodeTransform::new(
315                "Convert &str to String",
316                r#"(".*")"#,
317                r#"$1.to_string()"#,
318                r#"let s: String = "hello";"#,
319                r#"let s: String = "hello".to_string();"#,
320            ))
321            .with_transform(CodeTransform::new(
322                "Convert String to &str",
323                r"(\w+)",
324                "&$1",
325                "let s: &str = my_string;",
326                "let s: &str = &my_string;",
327            ))
328            .with_suggestions(&[
329                "Use `.to_string()` to create an owned String from &str",
330                "Use `&` or `.as_str()` to borrow a String as &str",
331                "Consider if you really need String or if &str would work",
332            ])
333            .with_priority(85)
334            .build(),
335    );
336
337    // Option/Result unwrapping
338    registry.register(
339        FixTemplate::builder("type-option-result", "Option/Result Type Handling", ErrorCategory::TypeMismatch)
340            .with_keywords(&["Option", "Result", "Some", "None", "Ok", "Err", "expected", "found"])
341            .with_explanation(
342                "Option and Result types wrap values. You need to unwrap them to access the inner value. \
343                 Prefer `?` operator, `map`, `and_then`, or pattern matching over `unwrap()`."
344            )
345            .with_transform(CodeTransform::new(
346                "Use ? operator",
347                r"(\w+)\.unwrap\(\)",
348                "$1?",
349                "let value = some_option.unwrap();",
350                "let value = some_option?;",
351            ))
352            .with_suggestions(&[
353                "Use `?` operator to propagate errors",
354                "Use `if let Some(x) = ...` for optional unwrapping",
355                "Use `.unwrap_or_default()` for safe defaults",
356                "Use `.expect(\"message\")` for better error messages",
357            ])
358            .with_docs(&[
359                "https://doc.rust-lang.org/std/option/",
360                "https://doc.rust-lang.org/std/result/",
361            ])
362            .with_priority(90)
363            .build(),
364    );
365}
366
367fn register_borrow_checker_templates(registry: &mut FixTemplateRegistry) {
368    // Cannot move out of borrowed
369    registry.register(
370        FixTemplate::builder(
371            "borrow-move",
372            "Cannot Move Out of Borrowed",
373            ErrorCategory::BorrowChecker,
374        )
375        .with_keywords(&["cannot move", "borrowed", "move out of"])
376        .with_explanation(
377            "You're trying to take ownership of a value that is only borrowed. \
378                 You need to either clone the value or restructure your code.",
379        )
380        .with_transform(CodeTransform::new(
381            "Clone the value",
382            r"(\w+)",
383            "$1.clone()",
384            "let x = borrowed_value;",
385            "let x = borrowed_value.clone();",
386        ))
387        .with_suggestions(&[
388            "Clone the value if it implements Clone",
389            "Take ownership of the original instead of borrowing",
390            "Use a reference instead of owned value",
391            "Restructure to avoid needing ownership",
392        ])
393        .with_priority(85)
394        .build(),
395    );
396
397    // Value used after move
398    registry.register(
399        FixTemplate::builder(
400            "borrow-use-after-move",
401            "Value Used After Move",
402            ErrorCategory::BorrowChecker,
403        )
404        .with_keywords(&["value used", "after move", "moved", "borrowed"])
405        .with_explanation(
406            "Once a value is moved, it can no longer be used. Clone the value before moving, \
407                 or restructure your code to avoid the second use.",
408        )
409        .with_suggestions(&[
410            "Clone the value before the first use if you need it twice",
411            "Pass by reference instead of by value",
412            "Use Rc/Arc for shared ownership",
413            "Restructure to use the value only once",
414        ])
415        .with_docs(&["https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html"])
416        .with_priority(80)
417        .build(),
418    );
419
420    // Mutable borrow conflict
421    registry.register(
422        FixTemplate::builder(
423            "borrow-mut-conflict",
424            "Mutable Borrow Conflict",
425            ErrorCategory::BorrowChecker,
426        )
427        .with_keywords(&[
428            "mutable",
429            "immutable",
430            "borrow",
431            "cannot borrow",
432            "already borrowed",
433        ])
434        .with_explanation(
435            "Rust prevents having mutable and immutable borrows at the same time. \
436                 Restructure your code to separate the borrows or use interior mutability.",
437        )
438        .with_suggestions(&[
439            "Separate the mutable and immutable operations",
440            "Use interior mutability (Cell, RefCell, Mutex)",
441            "Clone data instead of borrowing",
442            "Restructure to avoid overlapping borrows",
443        ])
444        .with_docs(&["https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html"])
445        .with_priority(85)
446        .build(),
447    );
448}
449
450fn register_lifetime_templates(registry: &mut FixTemplateRegistry) {
451    // Does not live long enough
452    registry.register(
453        FixTemplate::builder(
454            "lifetime-short",
455            "Value Does Not Live Long Enough",
456            ErrorCategory::LifetimeError,
457        )
458        .with_keywords(&[
459            "does not live long enough",
460            "lifetime",
461            "dropped",
462            "borrowed value",
463        ])
464        .with_explanation(
465            "The borrowed value is dropped before the borrow ends. \
466                 You need to ensure the value lives as long as the reference.",
467        )
468        .with_suggestions(&[
469            "Move the value to a longer-lived scope",
470            "Return an owned value instead of a reference",
471            "Use 'static lifetime for truly long-lived data",
472            "Clone the data to create owned value",
473        ])
474        .with_priority(80)
475        .build(),
476    );
477
478    // Explicit lifetime needed
479    registry.register(
480        FixTemplate::builder(
481            "lifetime-explicit",
482            "Explicit Lifetime Annotation Needed",
483            ErrorCategory::LifetimeError,
484        )
485        .with_keywords(&["lifetime", "annotation", "explicit", "'a", "parameter"])
486        .with_explanation(
487            "The compiler cannot infer the lifetime relationship. \
488                 Add explicit lifetime parameters to clarify.",
489        )
490        .with_transform(CodeTransform::new(
491            "Add lifetime parameter",
492            r"fn (\w+)\((.*)\) -> &(\w+)",
493            "fn $1<'a>($2) -> &'a $3",
494            "fn get_str(s: &String) -> &str",
495            "fn get_str<'a>(s: &'a String) -> &'a str",
496        ))
497        .with_suggestions(&[
498            "Add lifetime parameter <'a> to function signature",
499            "Annotate references with the same lifetime",
500            "Consider returning owned data instead",
501        ])
502        .with_docs(&["https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html"])
503        .with_priority(75)
504        .build(),
505    );
506}
507
508fn register_trait_bound_templates(registry: &mut FixTemplateRegistry) {
509    // Trait not implemented
510    registry.register(
511        FixTemplate::builder(
512            "trait-not-impl",
513            "Trait Not Implemented",
514            ErrorCategory::TraitBound,
515        )
516        .with_keywords(&["trait", "not implemented", "bound", "doesn't implement"])
517        .with_explanation(
518            "The type doesn't implement a required trait. \
519                 Either implement the trait or use a different approach.",
520        )
521        .with_suggestions(&[
522            "Derive the trait if possible: #[derive(Clone, Debug, ...)]",
523            "Implement the trait manually",
524            "Use a wrapper type that implements the trait",
525            "Change the function to not require the trait",
526        ])
527        .with_priority(80)
528        .build(),
529    );
530
531    // Missing derive
532    registry.register(
533        FixTemplate::builder(
534            "trait-derive",
535            "Missing Derive Attribute",
536            ErrorCategory::TraitBound,
537        )
538        .with_keywords(&["Clone", "Copy", "Debug", "Default", "derive", "cannot"])
539        .with_explanation(
540            "Common traits like Clone, Copy, Debug can be automatically derived. \
541                 Add #[derive(...)] to your struct or enum.",
542        )
543        .with_transform(CodeTransform::new(
544            "Add derive attribute",
545            r"struct (\w+)",
546            "#[derive(Clone, Debug)]\nstruct $1",
547            "struct MyStruct { ... }",
548            "#[derive(Clone, Debug)]\nstruct MyStruct { ... }",
549        ))
550        .with_suggestions(&[
551            "Add #[derive(Clone)] for Clone trait",
552            "Add #[derive(Debug)] for Debug trait",
553            "Add #[derive(Default)] for Default trait",
554            "Combine derives: #[derive(Clone, Debug, Default)]",
555        ])
556        .with_priority(85)
557        .build(),
558    );
559}
560
561fn register_import_templates(registry: &mut FixTemplateRegistry) {
562    // Not found in scope
563    registry.register(
564        FixTemplate::builder(
565            "import-not-found",
566            "Item Not Found in Scope",
567            ErrorCategory::MissingImport,
568        )
569        .with_keywords(&[
570            "not found",
571            "cannot find",
572            "unresolved",
573            "use of undeclared",
574        ])
575        .with_explanation(
576            "The item is not in scope. You need to import it with `use` \
577                 or use the fully qualified path.",
578        )
579        .with_suggestions(&[
580            "Add `use` statement at the top of the file",
581            "Use fully qualified path: std::collections::HashMap",
582            "Check if the crate is in Cargo.toml dependencies",
583            "Verify the item exists in that module",
584        ])
585        .with_priority(80)
586        .build(),
587    );
588
589    // Common std imports
590    registry.register(
591        FixTemplate::builder(
592            "import-std-common",
593            "Common Standard Library Imports",
594            ErrorCategory::MissingImport,
595        )
596        .with_keywords(&[
597            "HashMap", "HashSet", "Vec", "String", "Box", "Rc", "Arc", "Cell", "RefCell",
598        ])
599        .with_explanation("Common standard library types need to be imported.")
600        .with_transform(CodeTransform::new(
601            "Import HashMap",
602            "HashMap",
603            "use std::collections::HashMap;\n\nHashMap",
604            "let map: HashMap<K, V>",
605            "use std::collections::HashMap;\n\nlet map: HashMap<K, V>",
606        ))
607        .with_suggestions(&[
608            "use std::collections::{HashMap, HashSet};",
609            "use std::rc::Rc;",
610            "use std::sync::Arc;",
611            "use std::cell::{Cell, RefCell};",
612        ])
613        .with_priority(85)
614        .build(),
615    );
616}
617
618fn register_syntax_templates(registry: &mut FixTemplateRegistry) {
619    // Missing semicolon
620    registry.register(
621        FixTemplate::builder(
622            "syntax-semicolon",
623            "Missing Semicolon",
624            ErrorCategory::SyntaxError,
625        )
626        .with_keywords(&["expected", ";", "semicolon", "statement"])
627        .with_explanation(
628            "Statements in Rust must end with a semicolon (;). \
629                 Expressions that are returned don't need semicolons.",
630        )
631        .with_suggestions(&[
632            "Add semicolon at end of statement",
633            "If this is a return expression, remove the semicolon",
634            "Check for unmatched brackets or braces above",
635        ])
636        .with_priority(90)
637        .build(),
638    );
639
640    // Unmatched brackets
641    registry.register(
642        FixTemplate::builder(
643            "syntax-brackets",
644            "Unmatched Brackets",
645            ErrorCategory::SyntaxError,
646        )
647        .with_keywords(&["expected", "}", ")", "]", "unmatched", "unclosed"])
648        .with_explanation(
649            "Opening and closing brackets must be balanced. \
650                 Check for missing or extra brackets.",
651        )
652        .with_suggestions(&[
653            "Count opening and closing brackets",
654            "Use editor bracket matching feature",
655            "Check recent changes for missing brackets",
656        ])
657        .with_priority(85)
658        .build(),
659    );
660}
661
662// ============================================
663// DEPYLER-1309: Transpiler-Specific Patterns
664// Bootstrap the top 50 error patterns from corpus analysis
665// ============================================
666
667fn register_transpiler_patterns(registry: &mut FixTemplateRegistry) {
668    // === E0599: No method found patterns ===
669
670    // Pattern 1: datetime methods on tuples (hour, minute, second)
671    registry.register(
672        FixTemplate::builder(
673            "e0599-datetime-tuple",
674            "Datetime Methods on Tuple",
675            ErrorCategory::TypeMismatch,
676        )
677        .with_keywords(&["no method named", "hour", "minute", "second", "tuple"])
678        .with_explanation(
679            "Python datetime.time objects have hour/minute/second attributes. \
680             The transpiler incorrectly inferred a tuple type instead of a time type.",
681        )
682        .with_suggestions(&[
683            "Change type annotation from tuple to chrono::NaiveTime",
684            "Use tuple destructuring: let (hour, minute, second) = time;",
685            "Access tuple elements: time.0, time.1, time.2",
686        ])
687        .with_priority(90)
688        .build(),
689    );
690
691    // Pattern 2: year/month/day on tuples
692    registry.register(
693        FixTemplate::builder(
694            "e0599-date-tuple",
695            "Date Methods on Tuple",
696            ErrorCategory::TypeMismatch,
697        )
698        .with_keywords(&["no method named", "year", "month", "day", "tuple"])
699        .with_explanation(
700            "Python datetime.date objects have year/month/day attributes. \
701             The transpiler incorrectly inferred a tuple type instead of a date type.",
702        )
703        .with_suggestions(&[
704            "Change type annotation from tuple to chrono::NaiveDate",
705            "Use tuple destructuring: let (year, month, day) = date;",
706            "Access tuple elements: date.0, date.1, date.2",
707        ])
708        .with_priority(90)
709        .build(),
710    );
711
712    // Pattern 3: as_i64 doesn't exist (should be cast)
713    registry.register(
714        FixTemplate::builder(
715            "e0599-as-i64-cast",
716            "as_i64 Method Missing",
717            ErrorCategory::TypeMismatch,
718        )
719        .with_keywords(&["no method named", "as_i64", "i32", "i64"])
720        .with_explanation(
721            "Rust doesn't have an as_i64() method. Use the `as` keyword for numeric casts.",
722        )
723        .with_transform(CodeTransform::new(
724            "Replace .as_i64() with cast",
725            r"\.as_i64\(\)",
726            " as i64",
727            "value.as_i64()",
728            "value as i64",
729        ))
730        .with_suggestions(&[
731            "Use `value as i64` instead of `value.as_i64()`",
732            "For Option<i64>, use `.map(|v| v as i64)`",
733        ])
734        .with_priority(95)
735        .build(),
736    );
737
738    // Pattern 4: is_some on Vec (should be Option)
739    registry.register(
740        FixTemplate::builder(
741            "e0599-is-some-vec",
742            "is_some on Vec",
743            ErrorCategory::TypeMismatch,
744        )
745        .with_keywords(&["no method named", "is_some", "Vec"])
746        .with_explanation(
747            "is_some() is an Option method, not a Vec method. \
748             The transpiler incorrectly inferred Vec instead of Option.",
749        )
750        .with_suggestions(&[
751            "Change Vec<T> to Option<T> if checking for presence",
752            "Use !vec.is_empty() to check if Vec has elements",
753            "Wrap the Vec in Option if it's optional: Option<Vec<T>>",
754        ])
755        .with_priority(90)
756        .build(),
757    );
758
759    // Pattern 5: display on String (should be Path)
760    registry.register(
761        FixTemplate::builder(
762            "e0599-display-string",
763            "display on String",
764            ErrorCategory::TypeMismatch,
765        )
766        .with_keywords(&["no method named", "display", "String"])
767        .with_explanation(
768            "display() is a Path method, not a String method. \
769             For String formatting, just use the String directly or &str.",
770        )
771        .with_suggestions(&[
772            "Remove .display() - String implements Display directly",
773            "If this should be a Path, change the type to PathBuf",
774            "Use std::path::Path::new(&string).display()",
775        ])
776        .with_priority(85)
777        .build(),
778    );
779
780    // Pattern 6: ok on ReadDir
781    registry.register(
782        FixTemplate::builder(
783            "e0599-ok-readdir",
784            "ok() on ReadDir",
785            ErrorCategory::TypeMismatch,
786        )
787        .with_keywords(&["no method named", "ok", "ReadDir"])
788        .with_explanation(
789            "ReadDir doesn't have an ok() method. It's already a Result that was unwrapped. \
790             The ok() call is redundant.",
791        )
792        .with_suggestions(&[
793            "Remove the .ok() call - the iterator is already available",
794            "If the fs::read_dir() result needs error handling, use ? or match",
795        ])
796        .with_priority(85)
797        .build(),
798    );
799
800    // Pattern 7: duration_since on datetime types
801    registry.register(
802        FixTemplate::builder(
803            "e0599-duration-since",
804            "duration_since Missing",
805            ErrorCategory::TypeMismatch,
806        )
807        .with_keywords(&["no method named", "duration_since", "DateTime"])
808        .with_explanation(
809            "Python's timedelta arithmetic uses the - operator. \
810             In Rust, use signed_duration_since() or subtract with chrono types.",
811        )
812        .with_suggestions(&[
813            "Use .signed_duration_since(other) for chrono types",
814            "Use datetime1 - datetime2 for Duration",
815            "Check if the type is std::time::Instant vs chrono::DateTime",
816        ])
817        .with_priority(85)
818        .build(),
819    );
820
821    // Pattern 8: toordinal on date types
822    registry.register(
823        FixTemplate::builder(
824            "e0599-toordinal",
825            "toordinal Missing",
826            ErrorCategory::TypeMismatch,
827        )
828        .with_keywords(&["no method named", "toordinal"])
829        .with_explanation(
830            "Python's date.toordinal() returns days since year 1. \
831             In Rust/chrono, use .num_days_from_ce() for similar functionality.",
832        )
833        .with_suggestions(&[
834            "Use .num_days_from_ce() for chrono NaiveDate",
835            "Calculate manually: (date - NaiveDate::from_ymd(1, 1, 1)).num_days()",
836        ])
837        .with_priority(80)
838        .build(),
839    );
840
841    // Pattern 9: replace on date types
842    registry.register(
843        FixTemplate::builder(
844            "e0599-date-replace",
845            "date replace Missing",
846            ErrorCategory::TypeMismatch,
847        )
848        .with_keywords(&["no method named", "replace", "Date"])
849        .with_explanation(
850            "Python's date.replace() creates a new date with some fields changed. \
851             In Rust/chrono, use .with_year(), .with_month(), .with_day().",
852        )
853        .with_suggestions(&[
854            "Use .with_year(2024) to change year",
855            "Use .with_month(6) to change month",
856            "Use .with_day(15) to change day",
857            "Chain multiple: date.with_year(2024).with_month(6)",
858        ])
859        .with_priority(80)
860        .build(),
861    );
862
863    // === E0308: Type mismatch patterns ===
864
865    // Pattern 10: Vec to &[T] conversion
866    registry.register(
867        FixTemplate::builder(
868            "e0308-vec-slice",
869            "Vec to Slice Conversion",
870            ErrorCategory::TypeMismatch,
871        )
872        .with_keywords(&["expected", "&[", "found", "Vec"])
873        .with_explanation(
874            "Function expects a slice &[T] but received Vec<T>. \
875             Use &vec or vec.as_slice() to convert.",
876        )
877        .with_transform(CodeTransform::new(
878            "Convert Vec to slice",
879            r"(\w+)",
880            "&$1",
881            "function(my_vec)",
882            "function(&my_vec)",
883        ))
884        .with_suggestions(&[
885            "Use &vec to borrow as slice",
886            "Use vec.as_slice() for explicit conversion",
887        ])
888        .with_priority(90)
889        .build(),
890    );
891
892    // Pattern 11: Wrong argument count
893    registry.register(
894        FixTemplate::builder(
895            "e0308-arg-count",
896            "Arguments to Function Incorrect",
897            ErrorCategory::TypeMismatch,
898        )
899        .with_keywords(&["arguments to this function are incorrect"])
900        .with_explanation(
901            "The function is being called with wrong number or types of arguments. \
902             Check the function signature and adjust the call.",
903        )
904        .with_suggestions(&[
905            "Check the function signature for expected argument types",
906            "Verify argument order matches the function definition",
907            "Add missing arguments or remove extra ones",
908        ])
909        .with_priority(85)
910        .build(),
911    );
912
913    // Pattern 12: DepylerValue type inference
914    registry.register(
915        FixTemplate::builder(
916            "e0308-depyler-value",
917            "DepylerValue Type Inference",
918            ErrorCategory::TypeMismatch,
919        )
920        .with_keywords(&["DepylerValue", "expected", "found"])
921        .with_explanation(
922            "DepylerValue is a dynamic type wrapper. The transpiler needs better type \
923             inference to determine the concrete type at compile time.",
924        )
925        .with_suggestions(&[
926            "Add type annotation to help inference",
927            "Use .to_i64(), .to_f64(), .to_string() to extract concrete type",
928            "Check Python source for type hints",
929        ])
930        .with_priority(85)
931        .build(),
932    );
933
934    // === E0277: Trait not implemented patterns ===
935
936    // Pattern 13: AsRef<OsStr> not implemented
937    registry.register(
938        FixTemplate::builder(
939            "e0277-asref-osstr",
940            "AsRef<OsStr> Not Implemented",
941            ErrorCategory::TraitBound,
942        )
943        .with_keywords(&["AsRef<OsStr>", "not implemented", "Value"])
944        .with_explanation(
945            "subprocess/Command functions expect string-like types that implement AsRef<OsStr>. \
946             serde_json::Value doesn't implement this.",
947        )
948        .with_suggestions(&[
949            "Convert to String first: value.as_str().unwrap().to_string()",
950            "Change type inference to Vec<String> instead of Vec<Value>",
951            "Use explicit type annotation: let args: Vec<String>",
952        ])
953        .with_priority(90)
954        .build(),
955    );
956
957    // Pattern 14: Iterator trait missing
958    registry.register(
959        FixTemplate::builder(
960            "e0277-iterator",
961            "Iterator Trait Not Implemented",
962            ErrorCategory::TraitBound,
963        )
964        .with_keywords(&["Iterator", "not implemented", "for loop"])
965        .with_explanation(
966            "The for loop requires an Iterator. The type doesn't implement IntoIterator.",
967        )
968        .with_suggestions(&[
969            "Use .iter() for borrowed iteration",
970            "Use .into_iter() for consuming iteration",
971            "Check if the type should be a collection",
972        ])
973        .with_priority(85)
974        .build(),
975    );
976
977    // Pattern 15: Display trait missing
978    registry.register(
979        FixTemplate::builder(
980            "e0277-display",
981            "Display Trait Not Implemented",
982            ErrorCategory::TraitBound,
983        )
984        .with_keywords(&["Display", "not implemented", "format", "println"])
985        .with_explanation(
986            "The type doesn't implement Display for formatting with {} in format strings.",
987        )
988        .with_suggestions(&[
989            "Use {:?} for Debug formatting instead",
990            "Implement Display trait for the type",
991            "Convert to String first",
992        ])
993        .with_priority(80)
994        .build(),
995    );
996
997    // === E0609: No field on type patterns ===
998
999    // Pattern 16: No field on tuple
1000    registry.register(
1001        FixTemplate::builder(
1002            "e0609-tuple-field",
1003            "No Named Field on Tuple",
1004            ErrorCategory::TypeMismatch,
1005        )
1006        .with_keywords(&["no field", "tuple", "did you mean"])
1007        .with_explanation(
1008            "Tuples use numeric indices (0, 1, 2), not named fields. \
1009             For named fields, use a struct.",
1010        )
1011        .with_suggestions(&[
1012            "Use tuple.0, tuple.1, etc. for positional access",
1013            "Destructure: let (a, b, c) = tuple;",
1014            "Consider using a struct with named fields",
1015        ])
1016        .with_priority(85)
1017        .build(),
1018    );
1019
1020    // Pattern 17: No field on reference
1021    registry.register(
1022        FixTemplate::builder(
1023            "e0609-ref-field",
1024            "Field Access on Reference",
1025            ErrorCategory::TypeMismatch,
1026        )
1027        .with_keywords(&["no field", "&", "reference"])
1028        .with_explanation(
1029            "Dereferencing happens automatically for method calls but not for field access. \
1030             The type might be a reference to something without that field.",
1031        )
1032        .with_suggestions(&[
1033            "Dereference explicitly: (*ref).field",
1034            "Check if the base type has the field",
1035            "The reference might be to a different type than expected",
1036        ])
1037        .with_priority(80)
1038        .build(),
1039    );
1040
1041    // === E0282: Type annotation needed patterns ===
1042
1043    // Pattern 18: Cannot infer type
1044    registry.register(
1045        FixTemplate::builder(
1046            "e0282-cannot-infer",
1047            "Cannot Infer Type",
1048            ErrorCategory::TypeMismatch,
1049        )
1050        .with_keywords(&["cannot infer type", "type annotation needed"])
1051        .with_explanation(
1052            "Rust's type inference couldn't determine the type. Add explicit annotations.",
1053        )
1054        .with_suggestions(&[
1055            "Add type annotation: let x: i32 = ...;",
1056            "Use turbofish syntax: collect::<Vec<_>>()",
1057            "Provide more context with explicit types",
1058        ])
1059        .with_priority(85)
1060        .build(),
1061    );
1062
1063    // Pattern 19: Collection type ambiguous
1064    registry.register(
1065        FixTemplate::builder(
1066            "e0282-collect",
1067            "Collect Type Ambiguous",
1068            ErrorCategory::TypeMismatch,
1069        )
1070        .with_keywords(&["collect", "cannot infer", "type"])
1071        .with_explanation(
1072            "The collect() method can produce many container types. Specify which one.",
1073        )
1074        .with_transform(CodeTransform::new(
1075            "Add turbofish to collect",
1076            r"\.collect\(\)",
1077            ".collect::<Vec<_>>()",
1078            "iter.map(f).collect()",
1079            "iter.map(f).collect::<Vec<_>>()",
1080        ))
1081        .with_suggestions(&[
1082            "Use .collect::<Vec<_>>() for vectors",
1083            "Use .collect::<HashMap<_, _>>() for maps",
1084            "Use .collect::<HashSet<_>>() for sets",
1085        ])
1086        .with_priority(90)
1087        .build(),
1088    );
1089
1090    // === E0061: Wrong argument count patterns ===
1091
1092    // Pattern 20: This function takes N arguments
1093    registry.register(
1094        FixTemplate::builder(
1095            "e0061-arg-count",
1096            "Wrong Number of Arguments",
1097            ErrorCategory::SyntaxError,
1098        )
1099        .with_keywords(&["this function takes", "arguments", "supplied"])
1100        .with_explanation("The function was called with the wrong number of arguments.")
1101        .with_suggestions(&[
1102            "Check the function definition for required arguments",
1103            "Some Python default arguments may need explicit values in Rust",
1104            "Optional parameters might need Option<T> wrapping",
1105        ])
1106        .with_priority(85)
1107        .build(),
1108    );
1109
1110    // === E0605: Invalid cast patterns ===
1111
1112    // Pattern 21: Non-primitive cast
1113    registry.register(
1114        FixTemplate::builder(
1115            "e0605-non-primitive",
1116            "Non-Primitive Cast",
1117            ErrorCategory::TypeMismatch,
1118        )
1119        .with_keywords(&["non-primitive cast", "as", "cannot cast"])
1120        .with_explanation(
1121            "The `as` keyword only works for primitive types. Use From/Into traits \
1122             or constructor methods for complex types.",
1123        )
1124        .with_suggestions(&[
1125            "Use .into() for types implementing From/Into",
1126            "Use Type::from(value) explicitly",
1127            "Implement From trait for custom conversions",
1128        ])
1129        .with_priority(80)
1130        .build(),
1131    );
1132
1133    // === E0369: Binary operation not applicable patterns ===
1134
1135    // Pattern 22: Cannot apply binary operation
1136    registry.register(
1137        FixTemplate::builder(
1138            "e0369-binary-op",
1139            "Binary Operation Not Applicable",
1140            ErrorCategory::TypeMismatch,
1141        )
1142        .with_keywords(&["cannot be applied to type", "binary operation"])
1143        .with_explanation(
1144            "The binary operator (+, -, *, /, etc.) isn't defined for these types. \
1145             Implement the corresponding trait or convert types.",
1146        )
1147        .with_suggestions(&[
1148            "Convert types to match (both i32, both f64, etc.)",
1149            "Use PyOps traits for Python-style operations",
1150            "Implement Add/Sub/Mul/Div traits for custom types",
1151        ])
1152        .with_priority(80)
1153        .build(),
1154    );
1155
1156    // === E0600: Unary operation not applicable patterns ===
1157
1158    // Pattern 23: Cannot apply unary operator
1159    registry.register(
1160        FixTemplate::builder(
1161            "e0600-unary-op",
1162            "Unary Operation Not Applicable",
1163            ErrorCategory::TypeMismatch,
1164        )
1165        .with_keywords(&["cannot apply unary operator", "-", "!"])
1166        .with_explanation(
1167            "The unary operator (-, !) isn't defined for this type. \
1168             Check the type and implement Neg or Not traits if needed.",
1169        )
1170        .with_suggestions(&[
1171            "Check if the type is correct for negation",
1172            "Use explicit conversion before applying operator",
1173            "Implement Neg/Not traits for custom types",
1174        ])
1175        .with_priority(75)
1176        .build(),
1177    );
1178
1179    // === E0425: Unresolved name patterns ===
1180
1181    // Pattern 24: Not found in this scope
1182    registry.register(
1183        FixTemplate::builder(
1184            "e0425-not-found",
1185            "Name Not Found in Scope",
1186            ErrorCategory::MissingImport,
1187        )
1188        .with_keywords(&["cannot find value", "in this scope"])
1189        .with_explanation("The variable or function is not defined or not in scope.")
1190        .with_suggestions(&[
1191            "Check for typos in the name",
1192            "Import the item with `use`",
1193            "Ensure the variable is defined before use",
1194        ])
1195        .with_priority(85)
1196        .build(),
1197    );
1198
1199    // === Additional common transpiler patterns ===
1200
1201    // Pattern 25: PyOps trait missing
1202    registry.register(
1203        FixTemplate::builder(
1204            "e0599-pyops-missing",
1205            "PyOps Trait Method Missing",
1206            ErrorCategory::TraitBound,
1207        )
1208        .with_keywords(&["no method named", "py_add", "py_sub", "py_mul", "py_div"])
1209        .with_explanation(
1210            "Python-style operations require PyOps traits. The transpiler should \
1211             generate these trait implementations inline.",
1212        )
1213        .with_suggestions(&[
1214            "Check if PyOps traits are included in generated code",
1215            "Verify the types implement the required PyOps trait",
1216            "For Vec operations, ensure element-wise impls exist",
1217        ])
1218        .with_priority(95)
1219        .build(),
1220    );
1221
1222    // Pattern 26: serde_json::Value method missing
1223    registry.register(
1224        FixTemplate::builder(
1225            "e0599-json-value",
1226            "serde_json::Value Method Missing",
1227            ErrorCategory::TypeMismatch,
1228        )
1229        .with_keywords(&["no method named", "Value", "serde_json"])
1230        .with_explanation(
1231            "serde_json::Value is a dynamic JSON type. Python dict methods don't \
1232             map directly. Use as_object(), as_array(), etc.",
1233        )
1234        .with_suggestions(&[
1235            "Use .as_object() to get Option<&Map>",
1236            "Use .as_array() to get Option<&Vec>",
1237            "Use .as_str(), .as_i64(), .as_f64() for primitives",
1238            "Use .get(key) for dictionary-like access",
1239        ])
1240        .with_priority(90)
1241        .build(),
1242    );
1243
1244    // Pattern 27: std::time vs chrono confusion
1245    registry.register(
1246        FixTemplate::builder(
1247            "e0308-time-types",
1248            "Time Type Mismatch",
1249            ErrorCategory::TypeMismatch,
1250        )
1251        .with_keywords(&["std::time", "chrono", "Duration", "Instant"])
1252        .with_explanation(
1253            "Rust has two time libraries: std::time and chrono. They're not interchangeable. \
1254             Python datetime maps better to chrono types.",
1255        )
1256        .with_suggestions(&[
1257            "Use chrono for date/time: NaiveDate, NaiveTime, DateTime",
1258            "Use std::time for durations: Duration, Instant",
1259            "Convert: chrono::Duration::to_std() / from_std()",
1260        ])
1261        .with_priority(85)
1262        .build(),
1263    );
1264
1265    // Pattern 28: Optional argument handling
1266    registry.register(
1267        FixTemplate::builder(
1268            "e0308-optional-arg",
1269            "Optional Argument Type Mismatch",
1270            ErrorCategory::TypeMismatch,
1271        )
1272        .with_keywords(&["Option", "expected", "found", "argument"])
1273        .with_explanation(
1274            "Python's optional parameters with defaults need special handling. \
1275             Use Option<T> and provide defaults with .unwrap_or().",
1276        )
1277        .with_suggestions(&[
1278            "Wrap optional params in Option<T>",
1279            "Use .unwrap_or(default) for defaults",
1280            "Consider builder pattern for many optional args",
1281        ])
1282        .with_priority(80)
1283        .build(),
1284    );
1285
1286    // Pattern 29: Closure type inference
1287    registry.register(
1288        FixTemplate::builder(
1289            "e0282-closure",
1290            "Closure Type Cannot Be Inferred",
1291            ErrorCategory::TypeMismatch,
1292        )
1293        .with_keywords(&["closure", "cannot infer", "type"])
1294        .with_explanation(
1295            "Rust closures need type annotations when the type can't be inferred \
1296             from usage context.",
1297        )
1298        .with_suggestions(&[
1299            "Add parameter types: |x: i32| x + 1",
1300            "Add return type: |x| -> i32 { x + 1 }",
1301            "Use the closure immediately to provide context",
1302        ])
1303        .with_priority(75)
1304        .build(),
1305    );
1306
1307    // Pattern 30: String vs &str in HashMap keys
1308    registry.register(
1309        FixTemplate::builder(
1310            "e0308-hashmap-key",
1311            "HashMap Key Type Mismatch",
1312            ErrorCategory::TypeMismatch,
1313        )
1314        .with_keywords(&["HashMap", "expected", "&str", "String", "key"])
1315        .with_explanation(
1316            "HashMap<String, V> needs owned String keys, not &str. \
1317             Use .to_string() when inserting string literals.",
1318        )
1319        .with_transform(CodeTransform::new(
1320            "Convert &str key to String",
1321            r#""([^"]+)""#,
1322            r#""$1".to_string()"#,
1323            r#"map.insert("key", value);"#,
1324            r#"map.insert("key".to_string(), value);"#,
1325        ))
1326        .with_suggestions(&[
1327            "Use .to_string() for literal keys",
1328            "Consider HashMap<&str, V> if all keys are static",
1329        ])
1330        .with_priority(85)
1331        .build(),
1332    );
1333
1334    // Pattern 31: Vec::new() vs vec![] type inference
1335    registry.register(
1336        FixTemplate::builder(
1337            "e0282-vec-new",
1338            "Vec::new() Type Inference",
1339            ErrorCategory::TypeMismatch,
1340        )
1341        .with_keywords(&["Vec::new", "cannot infer", "type"])
1342        .with_explanation(
1343            "Vec::new() without elements can't infer the element type. \
1344             Add a type annotation or use vec![].",
1345        )
1346        .with_suggestions(&[
1347            "Add type: let v: Vec<i32> = Vec::new();",
1348            "Use turbofish: Vec::<i32>::new()",
1349            "Use vec![] with elements if possible",
1350        ])
1351        .with_priority(80)
1352        .build(),
1353    );
1354
1355    // Pattern 32: Result error type mismatch
1356    registry.register(
1357        FixTemplate::builder(
1358            "e0308-result-error",
1359            "Result Error Type Mismatch",
1360            ErrorCategory::TypeMismatch,
1361        )
1362        .with_keywords(&["Result", "expected", "found", "Err"])
1363        .with_explanation(
1364            "Different Result types have different error types. \
1365             Use .map_err() to convert between error types.",
1366        )
1367        .with_suggestions(&[
1368            "Use .map_err(|e| e.into()) for Into-compatible errors",
1369            "Use anyhow::Result for uniform error handling",
1370            "Consider using thiserror for custom error types",
1371        ])
1372        .with_priority(80)
1373        .build(),
1374    );
1375
1376    // Pattern 33: Index vs get for arrays/vectors
1377    registry.register(
1378        FixTemplate::builder(
1379            "e0277-index",
1380            "Index Trait Not Implemented",
1381            ErrorCategory::TraitBound,
1382        )
1383        .with_keywords(&["Index", "not implemented", "cannot index"])
1384        .with_explanation("The type doesn't support indexing with []. Use .get() for safe access.")
1385        .with_suggestions(&[
1386            "Use .get(index) which returns Option<&T>",
1387            "Check if the type should be a Vec or array",
1388            "For HashMap, use .get(&key)",
1389        ])
1390        .with_priority(80)
1391        .build(),
1392    );
1393
1394    // Pattern 34: Deref coercion failure
1395    registry.register(
1396        FixTemplate::builder(
1397            "e0308-deref",
1398            "Deref Coercion Failure",
1399            ErrorCategory::TypeMismatch,
1400        )
1401        .with_keywords(&["expected", "&", "found", "Box", "Rc", "Arc"])
1402        .with_explanation(
1403            "Smart pointers (Box, Rc, Arc) deref automatically but sometimes need explicit deref.",
1404        )
1405        .with_suggestions(&[
1406            "Use &*ptr to explicitly deref",
1407            "Use .as_ref() for &T from smart pointer",
1408            "Clone if you need an owned value",
1409        ])
1410        .with_priority(75)
1411        .build(),
1412    );
1413
1414    // Pattern 35: Lifetime elision failure
1415    registry.register(
1416        FixTemplate::builder(
1417            "e0106-lifetime",
1418            "Missing Lifetime Specifier",
1419            ErrorCategory::LifetimeError,
1420        )
1421        .with_keywords(&["missing lifetime specifier", "'"])
1422        .with_explanation(
1423            "The compiler can't infer the lifetime. Add explicit lifetime annotations.",
1424        )
1425        .with_suggestions(&[
1426            "Add lifetime parameter: fn foo<'a>(x: &'a str)",
1427            "Consider if you can use owned types instead",
1428            "Use 'static for literals and constants",
1429        ])
1430        .with_priority(80)
1431        .build(),
1432    );
1433}
1434
1435#[cfg(test)]
1436mod tests {
1437    use super::*;
1438
1439    // ===================
1440    // CodeTransform Tests
1441    // ===================
1442
1443    #[test]
1444    fn test_code_transform_creation() {
1445        let transform = CodeTransform::new(
1446            "Test transform",
1447            r"(\w+)",
1448            "$1.clone()",
1449            "let x = value;",
1450            "let x = value.clone();",
1451        );
1452
1453        assert_eq!(transform.description, "Test transform");
1454        assert!(!transform.match_pattern.is_empty());
1455        assert!(!transform.example_before.is_empty());
1456        assert!(!transform.example_after.is_empty());
1457    }
1458
1459    // ===================
1460    // FixTemplate Tests
1461    // ===================
1462
1463    #[test]
1464    fn test_fix_template_builder() {
1465        let template =
1466            FixTemplate::builder("test-id", "Test Template", ErrorCategory::TypeMismatch)
1467                .with_keywords(&["expected", "found"])
1468                .with_explanation("Test explanation")
1469                .with_suggestions(&["Suggestion 1", "Suggestion 2"])
1470                .with_priority(75)
1471                .build();
1472
1473        assert_eq!(template.id, "test-id");
1474        assert_eq!(template.name, "Test Template");
1475        assert_eq!(template.category, ErrorCategory::TypeMismatch);
1476        assert_eq!(template.trigger_keywords.len(), 2);
1477        assert_eq!(template.suggestions.len(), 2);
1478        assert_eq!(template.priority, 75);
1479    }
1480
1481    #[test]
1482    fn test_fix_template_matches() {
1483        let template = FixTemplate::builder("test", "Test", ErrorCategory::TypeMismatch)
1484            .with_keywords(&["expected", "found"])
1485            .build();
1486
1487        assert!(template.matches("error: expected i32, found str"));
1488        assert!(template.matches("EXPECTED TYPE"));
1489        assert!(!template.matches("no keywords here"));
1490    }
1491
1492    #[test]
1493    fn test_fix_template_match_score() {
1494        let template = FixTemplate::builder("test", "Test", ErrorCategory::TypeMismatch)
1495            .with_keywords(&["expected", "found", "type"])
1496            .with_priority(100)
1497            .build();
1498
1499        let score_all = template.match_score("expected type, found other type");
1500        let score_some = template.match_score("expected something");
1501        let score_none = template.match_score("no match");
1502
1503        assert!(score_all > score_some);
1504        assert!(score_some > score_none);
1505        assert!((score_none - 0.0).abs() < 1e-6);
1506    }
1507
1508    #[test]
1509    fn test_fix_template_empty_keywords() {
1510        let template = FixTemplate::builder("test", "Test", ErrorCategory::TypeMismatch).build();
1511
1512        let score = template.match_score("anything");
1513        assert!((score - 0.0).abs() < 1e-6);
1514    }
1515
1516    // ===================
1517    // FixTemplateRegistry Tests
1518    // ===================
1519
1520    #[test]
1521    fn test_registry_creation() {
1522        let registry = FixTemplateRegistry::new();
1523        assert_eq!(registry.template_count(), 0);
1524    }
1525
1526    #[test]
1527    fn test_registry_with_defaults() {
1528        let registry = FixTemplateRegistry::with_rust_defaults();
1529        assert!(registry.template_count() > 0);
1530    }
1531
1532    #[test]
1533    fn test_registry_register() {
1534        let mut registry = FixTemplateRegistry::new();
1535
1536        registry
1537            .register(FixTemplate::builder("test", "Test", ErrorCategory::TypeMismatch).build());
1538
1539        assert_eq!(registry.template_count(), 1);
1540    }
1541
1542    #[test]
1543    fn test_registry_get_templates() {
1544        let registry = FixTemplateRegistry::with_rust_defaults();
1545
1546        let type_templates = registry.get_templates(ErrorCategory::TypeMismatch);
1547        assert!(!type_templates.is_empty());
1548
1549        let borrow_templates = registry.get_templates(ErrorCategory::BorrowChecker);
1550        assert!(!borrow_templates.is_empty());
1551    }
1552
1553    #[test]
1554    fn test_registry_find_matches() {
1555        let registry = FixTemplateRegistry::with_rust_defaults();
1556
1557        let matches = registry.find_matches("expected i32, found &str");
1558        assert!(!matches.is_empty());
1559    }
1560
1561    #[test]
1562    fn test_registry_find_best_match() {
1563        let registry = FixTemplateRegistry::with_rust_defaults();
1564
1565        let best = registry.find_best_match("expected String, found &str");
1566        assert!(best.is_some());
1567    }
1568
1569    #[test]
1570    fn test_registry_no_match() {
1571        let registry = FixTemplateRegistry::with_rust_defaults();
1572
1573        let _matches = registry.find_matches("completely unrelated error xyz abc 123");
1574        // Might find some matches, but with low scores
1575        // The important thing is it doesn't panic
1576    }
1577
1578    #[test]
1579    fn test_registry_all_templates() {
1580        let registry = FixTemplateRegistry::with_rust_defaults();
1581
1582        let all = registry.all_templates();
1583        assert_eq!(all.len(), registry.template_count());
1584    }
1585
1586    // ===================
1587    // Default Template Tests
1588    // ===================
1589
1590    #[test]
1591    fn test_type_mismatch_templates() {
1592        let registry = FixTemplateRegistry::with_rust_defaults();
1593        let templates = registry.get_templates(ErrorCategory::TypeMismatch);
1594
1595        // Should have multiple type mismatch templates
1596        assert!(templates.len() >= 2);
1597
1598        // Check we have common ones
1599        let ids: Vec<&str> = templates.iter().map(|t| t.id.as_str()).collect();
1600        assert!(ids.contains(&"type-int-convert"));
1601        assert!(ids.contains(&"type-string-convert"));
1602    }
1603
1604    #[test]
1605    fn test_borrow_checker_templates() {
1606        let registry = FixTemplateRegistry::with_rust_defaults();
1607        let templates = registry.get_templates(ErrorCategory::BorrowChecker);
1608
1609        assert!(!templates.is_empty());
1610    }
1611
1612    #[test]
1613    fn test_lifetime_templates() {
1614        let registry = FixTemplateRegistry::with_rust_defaults();
1615        let templates = registry.get_templates(ErrorCategory::LifetimeError);
1616
1617        assert!(!templates.is_empty());
1618    }
1619
1620    #[test]
1621    fn test_trait_bound_templates() {
1622        let registry = FixTemplateRegistry::with_rust_defaults();
1623        let templates = registry.get_templates(ErrorCategory::TraitBound);
1624
1625        assert!(!templates.is_empty());
1626    }
1627
1628    #[test]
1629    fn test_import_templates() {
1630        let registry = FixTemplateRegistry::with_rust_defaults();
1631        let templates = registry.get_templates(ErrorCategory::MissingImport);
1632
1633        assert!(!templates.is_empty());
1634    }
1635
1636    #[test]
1637    fn test_syntax_templates() {
1638        let registry = FixTemplateRegistry::with_rust_defaults();
1639        let templates = registry.get_templates(ErrorCategory::SyntaxError);
1640
1641        assert!(!templates.is_empty());
1642    }
1643
1644    // ===================
1645    // Integration Tests
1646    // ===================
1647
1648    #[test]
1649    fn test_string_conversion_match() {
1650        let registry = FixTemplateRegistry::with_rust_defaults();
1651
1652        let matches = registry.find_matches("error: expected `String`, found `&str`");
1653
1654        // Should find string conversion template
1655        let has_string_template = matches.iter().any(|t| t.id == "type-string-convert");
1656        assert!(has_string_template);
1657    }
1658
1659    #[test]
1660    fn test_borrow_move_match() {
1661        let registry = FixTemplateRegistry::with_rust_defaults();
1662
1663        let matches = registry.find_matches("cannot move out of borrowed content");
1664
1665        // Should find borrow-move template
1666        let has_borrow_template = matches.iter().any(|t| t.id == "borrow-move");
1667        assert!(has_borrow_template);
1668    }
1669
1670    #[test]
1671    fn test_template_has_suggestions() {
1672        let registry = FixTemplateRegistry::with_rust_defaults();
1673
1674        for template in registry.all_templates() {
1675            // Every template should have at least one suggestion
1676            assert!(
1677                !template.suggestions.is_empty(),
1678                "Template {} has no suggestions",
1679                template.id
1680            );
1681        }
1682    }
1683
1684    #[test]
1685    fn test_template_has_explanation() {
1686        let registry = FixTemplateRegistry::with_rust_defaults();
1687
1688        for template in registry.all_templates() {
1689            // Every template should have an explanation
1690            assert!(
1691                !template.explanation.is_empty(),
1692                "Template {} has no explanation",
1693                template.id
1694            );
1695        }
1696    }
1697}