use std::sync::Arc;
use rustc_hash::{FxHashMap, FxHashSet};
use crate::{
metadata::{
typesystem::CilType,
validation::{
context::{OwnedValidationContext, ValidationContext},
traits::OwnedValidator,
},
},
Error, Result,
};
pub struct OwnedCircularityValidator;
impl OwnedCircularityValidator {
#[must_use]
pub fn new() -> Self {
Self
}
fn validate_inheritance_cycles(&self, context: &OwnedValidationContext) -> Result<()> {
let mut visited = FxHashSet::default();
let mut visiting = FxHashSet::default();
for type_entry in context.all_types() {
let type_ptr = Arc::as_ptr(type_entry) as usize;
if !visited.contains(&type_ptr) {
self.check_inheritance_cycle_relationships(
type_entry,
&mut visited,
&mut visiting,
)?;
}
}
Ok(())
}
fn check_inheritance_cycle_relationships(
&self,
type_entry: &CilType,
visited: &mut FxHashSet<usize>,
visiting: &mut FxHashSet<usize>,
) -> Result<()> {
let type_ptr = std::ptr::from_ref::<CilType>(type_entry) as usize;
if visited.contains(&type_ptr) {
return Ok(());
}
if visiting.contains(&type_ptr) {
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!(
"Circular inheritance relationship detected: Type '{}' (token 0x{:08X}) is part of an inheritance cycle",
type_entry.name, type_entry.token.value()
),
});
}
visiting.insert(type_ptr);
if let Some(base_type) = type_entry.base() {
self.check_inheritance_cycle_relationships(&base_type, visited, visiting)?;
}
visiting.remove(&type_ptr);
visited.insert(type_ptr);
Ok(())
}
fn validate_interface_implementation_cycles(
&self,
context: &OwnedValidationContext,
) -> Result<()> {
let mut visited = FxHashSet::default();
let mut visiting = FxHashSet::default();
let interface_relationships = context.interface_relationships();
for type_entry in context.target_assembly_types() {
let type_ptr = Arc::as_ptr(type_entry) as usize;
if !visited.contains(&type_ptr) {
self.check_interface_implementation_cycle(
type_ptr,
interface_relationships,
&mut visited,
&mut visiting,
)?;
}
}
Ok(())
}
fn check_interface_implementation_cycle(
&self,
type_ptr: usize,
interface_relationships: &FxHashMap<usize, Vec<usize>>,
visited: &mut FxHashSet<usize>,
visiting: &mut FxHashSet<usize>,
) -> Result<()> {
if visited.contains(&type_ptr) {
return Ok(());
}
if visiting.contains(&type_ptr) {
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message:
"Circular interface implementation relationship detected: Type implements itself through interface chain"
.to_string(),
});
}
visiting.insert(type_ptr);
if let Some(implemented_ptrs) = interface_relationships.get(&type_ptr) {
for &implemented_ptr in implemented_ptrs {
self.check_interface_implementation_cycle(
implemented_ptr,
interface_relationships,
visited,
visiting,
)?;
}
}
visiting.remove(&type_ptr);
visited.insert(type_ptr);
Ok(())
}
fn validate_cross_reference_cycles(&self, context: &OwnedValidationContext) -> Result<()> {
let mut visited = FxHashSet::default();
let mut visiting = FxHashSet::default();
let target_types = context.target_assembly_types();
let mut reference_relationships: FxHashMap<usize, Vec<usize>> = FxHashMap::default();
for type_entry in target_types {
let type_ptr = Arc::as_ptr(type_entry) as usize;
let mut references = Vec::new();
if let Some(base_type) = type_entry.base() {
let base_ptr = Arc::as_ptr(&base_type) as usize;
if !base_type.fullname().starts_with("System.") {
references.push(base_ptr);
}
}
for (_, interface_ref) in type_entry.interfaces.iter() {
if let Some(interface_type) = interface_ref.upgrade() {
let interface_ptr = Arc::as_ptr(&interface_type) as usize;
if !interface_type.fullname().starts_with("System.") {
references.push(interface_ptr);
}
}
}
if !references.is_empty() {
reference_relationships.insert(type_ptr, references);
}
}
for type_entry in target_types {
let type_ptr = Arc::as_ptr(type_entry) as usize;
if !visited.contains(&type_ptr) {
self.check_cross_reference_cycle(
type_ptr,
&reference_relationships,
&mut visited,
&mut visiting,
)?;
}
}
Ok(())
}
fn check_cross_reference_cycle(
&self,
type_ptr: usize,
reference_relationships: &FxHashMap<usize, Vec<usize>>,
visited: &mut FxHashSet<usize>,
visiting: &mut FxHashSet<usize>,
) -> Result<()> {
if visited.contains(&type_ptr) {
return Ok(());
}
if visiting.contains(&type_ptr) {
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message:
"Circular cross-reference relationship detected: Type references itself through relationship chain"
.to_string(),
});
}
visiting.insert(type_ptr);
if let Some(referenced_ptrs) = reference_relationships.get(&type_ptr) {
for &referenced_ptr in referenced_ptrs {
self.check_cross_reference_cycle(
referenced_ptr,
reference_relationships,
visited,
visiting,
)?;
}
}
visiting.remove(&type_ptr);
visited.insert(type_ptr);
Ok(())
}
}
impl OwnedValidator for OwnedCircularityValidator {
fn validate_owned(&self, context: &OwnedValidationContext) -> Result<()> {
self.validate_inheritance_cycles(context)?;
self.validate_interface_implementation_cycles(context)?;
self.validate_cross_reference_cycles(context)?;
Ok(())
}
fn name(&self) -> &'static str {
"OwnedCircularityValidator"
}
fn priority(&self) -> u32 {
150
}
fn should_run(&self, context: &OwnedValidationContext) -> bool {
context.config().enable_cross_table_validation
}
}
impl Default for OwnedCircularityValidator {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[cfg_attr(feature = "skip-expensive-tests", allow(unused_imports))]
mod tests {
use super::*;
use crate::{
metadata::validation::ValidationConfig,
test::{
factories::validation::circularity::owned_circularity_validator_file_factory,
owned_validator_test,
},
};
#[test]
#[cfg(not(feature = "skip-expensive-tests"))]
fn test_owned_circularity_validator() -> Result<()> {
let validator = OwnedCircularityValidator::new();
let config = ValidationConfig {
enable_cross_table_validation: true,
..Default::default()
};
owned_validator_test(
owned_circularity_validator_file_factory,
"OwnedCircularityValidator",
"ValidationOwnedFailed",
config,
|context| validator.validate_owned(context),
)
}
}