use crate::{
metadata::{
tables::FieldAttributes,
validation::{
context::{OwnedValidationContext, ValidationContext},
traits::OwnedValidator,
},
},
Error, Result,
};
pub struct OwnedFieldValidator;
impl OwnedFieldValidator {
#[must_use]
pub fn new() -> Self {
Self
}
fn validate_field_signatures(&self, context: &OwnedValidationContext) -> Result<()> {
for type_entry in context.all_types() {
for (_, field) in type_entry.fields.iter() {
if field.name.is_empty() {
let token_value = field.token.value();
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!("Field with token 0x{token_value:08X} has empty name"),
});
}
if let crate::metadata::signatures::TypeSignature::Unknown = &field.signature.base {
let field_name = &field.name;
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!("Field '{field_name}' has unresolved type in signature"),
});
}
for (index, modifier) in field.signature.modifiers.iter().enumerate() {
if modifier.modifier_type.value() == 0 {
let field_name = &field.name;
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!(
"Field '{field_name}' modifier {index} has invalid token"
),
});
}
}
}
}
Ok(())
}
fn validate_field_accessibility(&self, context: &OwnedValidationContext) -> Result<()> {
for type_entry in context.all_types() {
for (_, field) in type_entry.fields.iter() {
let access_level = field.flags & FieldAttributes::FIELD_ACCESS_MASK;
match access_level {
FieldAttributes::COMPILER_CONTROLLED
| FieldAttributes::PRIVATE
| FieldAttributes::FAM_AND_ASSEM
| FieldAttributes::ASSEMBLY
| FieldAttributes::FAMILY
| FieldAttributes::FAM_OR_ASSEM
| FieldAttributes::PUBLIC => {
}
_ => {
let field_name = &field.name;
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!(
"Field '{field_name}' has invalid access level: 0x{access_level:02X}"
),
});
}
}
if field.flags & FieldAttributes::STATIC != 0
&& field.flags & FieldAttributes::INIT_ONLY != 0
{
}
if field.flags & 0x0040 != 0 && field.flags & FieldAttributes::STATIC == 0 {
let field_name = &field.name;
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!("Literal field '{field_name}' must also be static"),
});
}
}
}
Ok(())
}
fn validate_special_attributes(&self, context: &OwnedValidationContext) -> Result<()> {
for type_entry in context.all_types() {
for (_, field) in type_entry.fields.iter() {
if field.flags & 0x1000 != 0 { }
if field.flags & 0x0080 != 0 {
}
if field.flags & 0x2000 != 0 { }
if field.flags & 0x0040 != 0 { }
if field.flags & 0x0400 != 0 {
if field.flags & 0x0200 == 0 {
let field_name = &field.name;
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!(
"Field '{field_name}' has RTSpecialName but not SpecialName"
),
});
}
}
}
}
Ok(())
}
fn validate_field_naming(&self, context: &OwnedValidationContext) -> Result<()> {
for type_entry in context.all_types() {
for (_, field) in type_entry.fields.iter() {
if field.name.starts_with('<') && field.name.ends_with(">k__BackingField") {
let access_level = field.flags & FieldAttributes::FIELD_ACCESS_MASK;
if access_level != FieldAttributes::PRIVATE {
let field_name = &field.name;
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!("Backing field '{field_name}' should be private"),
});
}
}
if field.name.starts_with('<')
&& field.name.contains("Event")
&& field.flags & FieldAttributes::STATIC == 0
{
let access_level = field.flags & FieldAttributes::FIELD_ACCESS_MASK;
if access_level == FieldAttributes::PUBLIC {}
}
if field.name.contains('\0') {
let field_name = &field.name;
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!("Field '{field_name}' contains null character"),
});
}
}
}
Ok(())
}
}
impl OwnedValidator for OwnedFieldValidator {
fn validate_owned(&self, context: &OwnedValidationContext) -> Result<()> {
self.validate_field_signatures(context)?;
self.validate_field_accessibility(context)?;
self.validate_special_attributes(context)?;
self.validate_field_naming(context)?;
Ok(())
}
fn name(&self) -> &'static str {
"OwnedFieldValidator"
}
fn priority(&self) -> u32 {
155
}
fn should_run(&self, context: &OwnedValidationContext) -> bool {
context.config().enable_semantic_validation
}
}
impl Default for OwnedFieldValidator {
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::members_field::owned_field_validator_file_factory,
owned_validator_test,
},
};
#[test]
#[cfg(not(feature = "skip-expensive-tests"))]
fn test_owned_field_validator() -> Result<()> {
let validator = OwnedFieldValidator::new();
let config = ValidationConfig {
enable_semantic_validation: true,
..Default::default()
};
owned_validator_test(
owned_field_validator_file_factory,
"OwnedFieldValidator",
"ValidationOwnedFailed",
config,
|context| validator.validate_owned(context),
)
}
}