Skip to content

Commit 2356e9e

Browse files
committed
Semantic type diffing support
1 parent c844475 commit 2356e9e

File tree

3 files changed

+463
-10
lines changed

3 files changed

+463
-10
lines changed

crates/semantic-analysis/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ pub mod symbol_table;
77
pub mod symbol_resolver;
88
pub mod scope_manager;
99
pub mod type_system;
10+
pub mod type_extractor;
11+
pub mod type_dependency_graph;
1012
pub mod dependency_graph;
1113
pub mod analyzer;
1214

1315
pub use symbol_table::{SymbolTable, Symbol, SymbolKind, Scope, SymbolReference, ReferenceType, ScopeType, ScopeId};
1416
pub use symbol_resolver::{SymbolResolver, SymbolResolverConfig, ImportInfo, FileContext};
1517
pub use scope_manager::{ScopeManager, ScopeResolution, ScopeAnalysis};
16-
pub use type_system::{TypeInfo, TypeResolver, TypeEquivalence};
18+
pub use type_system::{TypeInfo, TypeResolver, TypeEquivalence, TypeSignature, TypeKind, FieldInfo, MethodInfo, Visibility};
19+
pub use type_extractor::{TypeExtractor, TypeExtractorConfig, ExtractedTypeInfo, TypeExtractionResult};
20+
pub use type_dependency_graph::{TypeDependencyGraphBuilder, TypeRelationship, TypeRelationshipType, TypeDependencyAnalysis, TypeCouplingMetrics};
1721
pub use dependency_graph::{DependencyGraph, DependencyNode, DependencyEdge};
1822
pub use analyzer::{SemanticAnalyzer, AnalysisResult, AnalysisError};
1923

crates/semantic-analysis/src/tests.rs

Lines changed: 233 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
//! Tests for semantic analysis components
22
33
use crate::{
4-
SymbolResolver, SymbolResolverConfig, ScopeManager, SymbolTable,
5-
Symbol, SymbolKind, ReferenceType, SymbolReference, ScopeType
4+
SymbolResolver, SymbolResolverConfig, ScopeManager, SymbolTable,
5+
Symbol, SymbolKind, ReferenceType, SymbolReference, ScopeType,
6+
TypeExtractor, TypeExtractorConfig, TypeSignature, TypeEquivalence,
7+
TypeDependencyGraphBuilder, TypeRelationshipType
68
};
79
use smart_diff_parser::{TreeSitterParser, Language, ParseResult};
810
use std::collections::HashSet;
@@ -380,3 +382,232 @@ mod symbol_table_tests {
380382
assert_eq!(references[0].reference_type, ReferenceType::Call);
381383
}
382384
}
385+
386+
#[cfg(test)]
387+
mod type_system_tests {
388+
use super::*;
389+
390+
#[test]
391+
fn test_type_signature_parsing() {
392+
// Test simple type
393+
let simple_type = TypeSignature::parse("String").unwrap();
394+
assert_eq!(simple_type.base_type, "String");
395+
assert!(simple_type.generic_params.is_empty());
396+
assert_eq!(simple_type.array_dimensions, 0);
397+
398+
// Test generic type
399+
let generic_type = TypeSignature::parse("List<String>").unwrap();
400+
assert_eq!(generic_type.base_type, "List");
401+
assert_eq!(generic_type.generic_params.len(), 1);
402+
assert_eq!(generic_type.generic_params[0].base_type, "String");
403+
404+
// Test nested generics
405+
let nested_generic = TypeSignature::parse("Map<String, List<Integer>>").unwrap();
406+
assert_eq!(nested_generic.base_type, "Map");
407+
assert_eq!(nested_generic.generic_params.len(), 2);
408+
assert_eq!(nested_generic.generic_params[0].base_type, "String");
409+
assert_eq!(nested_generic.generic_params[1].base_type, "List");
410+
assert_eq!(nested_generic.generic_params[1].generic_params[0].base_type, "Integer");
411+
412+
// Test array type
413+
let array_type = TypeSignature::parse("String[][]").unwrap();
414+
assert_eq!(array_type.base_type, "String");
415+
assert_eq!(array_type.array_dimensions, 2);
416+
417+
// Test nullable type
418+
let nullable_type = TypeSignature::parse("String?").unwrap();
419+
assert_eq!(nullable_type.base_type, "String");
420+
assert!(nullable_type.is_nullable);
421+
}
422+
423+
#[test]
424+
fn test_type_equivalence() {
425+
// Test exact match
426+
assert!(TypeEquivalence::are_equivalent("String", "String"));
427+
428+
// Test normalized equivalence
429+
assert!(TypeEquivalence::are_equivalent("int", "i32"));
430+
assert!(TypeEquivalence::are_equivalent("String", "string"));
431+
assert!(TypeEquivalence::are_equivalent("bool", "Boolean"));
432+
433+
// Test non-equivalent types
434+
assert!(!TypeEquivalence::are_equivalent("String", "Integer"));
435+
assert!(!TypeEquivalence::are_equivalent("int", "float"));
436+
}
437+
438+
#[test]
439+
fn test_complex_type_equivalence() {
440+
let type1 = TypeSignature::parse("List<String>").unwrap();
441+
let type2 = TypeSignature::parse("List<String>").unwrap();
442+
let type3 = TypeSignature::parse("List<Integer>").unwrap();
443+
444+
assert!(TypeEquivalence::are_complex_types_equivalent(&type1, &type2));
445+
assert!(!TypeEquivalence::are_complex_types_equivalent(&type1, &type3));
446+
}
447+
448+
#[test]
449+
fn test_type_similarity_calculation() {
450+
let type1 = TypeSignature::parse("List<String>").unwrap();
451+
let type2 = TypeSignature::parse("List<String>").unwrap();
452+
let type3 = TypeSignature::parse("List<Integer>").unwrap();
453+
let type4 = TypeSignature::parse("ArrayList<String>").unwrap();
454+
455+
// Identical types should have similarity 1.0
456+
assert_eq!(TypeEquivalence::calculate_type_similarity(&type1, &type2), 1.0);
457+
458+
// Different generic parameters should have lower similarity
459+
let similarity_diff_generic = TypeEquivalence::calculate_type_similarity(&type1, &type3);
460+
assert!(similarity_diff_generic < 1.0);
461+
assert!(similarity_diff_generic > 0.0);
462+
463+
// Related types should have some similarity
464+
let similarity_related = TypeEquivalence::calculate_type_similarity(&type1, &type4);
465+
assert!(similarity_related > 0.0);
466+
assert!(similarity_related < 1.0);
467+
}
468+
469+
#[test]
470+
fn test_type_signature_string_conversion() {
471+
let type_sig = TypeSignature::new("List".to_string())
472+
.with_generics(vec![TypeSignature::new("String".to_string())])
473+
.with_array_dimensions(1)
474+
.with_nullable(true);
475+
476+
let type_string = type_sig.to_string();
477+
assert!(type_string.contains("List"));
478+
assert!(type_string.contains("String"));
479+
assert!(type_string.contains("[]"));
480+
assert!(type_string.contains("?"));
481+
}
482+
}
483+
484+
#[cfg(test)]
485+
mod type_extractor_tests {
486+
use super::*;
487+
488+
#[test]
489+
fn test_type_extractor_creation() {
490+
let config = TypeExtractorConfig::default();
491+
let extractor = TypeExtractor::new(Language::Java, config);
492+
493+
// Basic smoke test
494+
assert!(true);
495+
}
496+
497+
#[test]
498+
fn test_java_type_parsing() {
499+
let extractor = TypeExtractor::with_defaults(Language::Java);
500+
501+
// Test simple type
502+
let simple_type = extractor.parse_type_signature("String").unwrap();
503+
assert_eq!(simple_type.base_type, "String");
504+
505+
// Test generic type
506+
let generic_type = extractor.parse_type_signature("List<String>").unwrap();
507+
assert_eq!(generic_type.base_type, "List");
508+
assert_eq!(generic_type.generic_params.len(), 1);
509+
assert_eq!(generic_type.generic_params[0].base_type, "String");
510+
}
511+
512+
#[test]
513+
fn test_python_type_parsing() {
514+
let extractor = TypeExtractor::with_defaults(Language::Python);
515+
516+
// Test Python list type
517+
let list_type = extractor.parse_type_signature("List[str]").unwrap();
518+
assert_eq!(list_type.base_type, "List");
519+
assert_eq!(list_type.generic_params.len(), 1);
520+
assert_eq!(list_type.generic_params[0].base_type, "str");
521+
522+
// Test Python dict type
523+
let dict_type = extractor.parse_type_signature("Dict[str, int]").unwrap();
524+
assert_eq!(dict_type.base_type, "Dict");
525+
assert_eq!(dict_type.generic_params.len(), 2);
526+
assert_eq!(dict_type.generic_params[0].base_type, "str");
527+
assert_eq!(dict_type.generic_params[1].base_type, "int");
528+
}
529+
530+
#[test]
531+
fn test_cpp_type_parsing() {
532+
let extractor = TypeExtractor::with_defaults(Language::Cpp);
533+
534+
// Test const pointer type
535+
let const_ptr_type = extractor.parse_type_signature("const int*").unwrap();
536+
assert_eq!(const_ptr_type.base_type, "const int*");
537+
assert!(const_ptr_type.modifiers.contains(&"const".to_string()));
538+
assert!(const_ptr_type.modifiers.contains(&"pointer".to_string()));
539+
540+
// Test reference type
541+
let ref_type = extractor.parse_type_signature("std::string&").unwrap();
542+
assert!(ref_type.modifiers.contains(&"reference".to_string()));
543+
}
544+
545+
#[test]
546+
fn test_primitive_type_detection() {
547+
let extractor = TypeExtractor::with_defaults(Language::Java);
548+
549+
assert!(extractor.is_primitive_type("int"));
550+
assert!(extractor.is_primitive_type("String"));
551+
assert!(extractor.is_primitive_type("boolean"));
552+
assert!(extractor.is_primitive_type("void"));
553+
554+
assert!(!extractor.is_primitive_type("ArrayList"));
555+
assert!(!extractor.is_primitive_type("MyCustomClass"));
556+
}
557+
558+
#[test]
559+
fn test_type_dependency_graph_building() {
560+
let extractor = TypeExtractor::with_defaults(Language::Java);
561+
562+
// Create mock extracted type info
563+
let mut extracted_types = Vec::new();
564+
565+
// This would normally come from actual type extraction
566+
// For now, just test that the method doesn't panic
567+
let dependencies = extractor.build_type_dependency_graph(&extracted_types);
568+
assert!(dependencies.is_empty());
569+
}
570+
}
571+
572+
#[cfg(test)]
573+
mod type_dependency_graph_tests {
574+
use super::*;
575+
576+
#[test]
577+
fn test_type_dependency_graph_creation() {
578+
let mut builder = TypeDependencyGraphBuilder::new();
579+
580+
// Basic smoke test
581+
assert_eq!(builder.get_type_info_map().len(), 0);
582+
}
583+
584+
#[test]
585+
fn test_type_relationship_types() {
586+
// Test that all relationship types are properly defined
587+
let inheritance = TypeRelationshipType::Inheritance;
588+
let implementation = TypeRelationshipType::Implementation;
589+
let composition = TypeRelationshipType::Composition;
590+
591+
assert_ne!(inheritance, implementation);
592+
assert_ne!(implementation, composition);
593+
assert_ne!(composition, inheritance);
594+
}
595+
596+
#[test]
597+
fn test_coupling_metrics_calculation() {
598+
// This would test the coupling metrics calculation
599+
// For now, just ensure the types are properly defined
600+
use crate::TypeCouplingMetrics;
601+
602+
let metrics = TypeCouplingMetrics {
603+
afferent_coupling: 5,
604+
efferent_coupling: 3,
605+
instability: 0.375, // 3 / (5 + 3)
606+
abstractness: 0.0,
607+
};
608+
609+
assert_eq!(metrics.afferent_coupling, 5);
610+
assert_eq!(metrics.efferent_coupling, 3);
611+
assert!((metrics.instability - 0.375).abs() < 0.001);
612+
}
613+
}

0 commit comments

Comments
 (0)