use crate::{
metadata::{
tables::{FieldAttributes, TypeAttributes},
validation::{
context::{OwnedValidationContext, ValidationContext},
traits::OwnedValidator,
},
},
Error, Result,
};
pub struct OwnedAccessibilityValidator;
impl OwnedAccessibilityValidator {
#[must_use]
pub fn new() -> Self {
Self
}
fn validate_type_accessibility(&self, context: &OwnedValidationContext) -> Result<()> {
for type_entry in context.all_types() {
let visibility = type_entry.flags & TypeAttributes::VISIBILITY_MASK;
match visibility {
TypeAttributes::NOT_PUBLIC
| TypeAttributes::PUBLIC
| TypeAttributes::NESTED_PUBLIC
| TypeAttributes::NESTED_PRIVATE
| TypeAttributes::NESTED_FAMILY
| TypeAttributes::NESTED_ASSEMBLY
| TypeAttributes::NESTED_FAM_AND_ASSEM
| TypeAttributes::NESTED_FAM_OR_ASSEM => {
}
_ => {
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!(
"Type '{}' has invalid visibility: 0x{:02X}",
type_entry.name, visibility
),
});
}
}
if type_entry.enclosing_type.get().is_some() {
if !(TypeAttributes::NESTED_PUBLIC..=TypeAttributes::NESTED_FAM_OR_ASSEM)
.contains(&visibility)
{
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!(
"Nested type '{}' has invalid visibility flags: 0x{:02X} (must be between NESTED_PUBLIC=0x02 and NESTED_FAM_OR_ASSEM=0x07)",
type_entry.name, visibility
),
});
}
}
if type_entry.flags & TypeAttributes::INTERFACE != 0
&& type_entry.flags & TypeAttributes::SEALED != 0
{
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!("Interface '{}' cannot be sealed", type_entry.name),
});
}
}
Ok(())
}
fn validate_member_accessibility(&self, context: &OwnedValidationContext) -> Result<()> {
for type_entry in context.all_types() {
let type_visibility = type_entry.flags & TypeAttributes::VISIBILITY_MASK;
for (_, method_ref) in type_entry.methods.iter() {
if let Some(method) = method_ref.upgrade() {
if method.name.is_empty() {
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!("Method in type '{}' has empty name", type_entry.name),
});
}
}
}
for (_, field) in type_entry.fields.iter() {
let field_access = field.flags & FieldAttributes::FIELD_ACCESS_MASK;
if field_access == FieldAttributes::PUBLIC
&& type_visibility == TypeAttributes::NOT_PUBLIC
{
}
if field.flags & 0x0040 != 0 && field.flags & FieldAttributes::STATIC == 0 {
let field_name = &field.name;
let type_name = &type_entry.name;
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!(
"Literal field '{field_name}' in type '{type_name}' must be static"
),
});
}
}
}
Ok(())
}
fn validate_interface_accessibility(&self, context: &OwnedValidationContext) -> Result<()> {
for type_entry in context.all_types() {
for (_, interface_ref) in type_entry.interfaces.iter() {
let type_visibility = type_entry.flags & TypeAttributes::VISIBILITY_MASK;
if let Some(interface_type) = interface_ref.upgrade() {
let interface_visibility =
interface_type.flags & TypeAttributes::VISIBILITY_MASK;
if interface_visibility == TypeAttributes::PUBLIC
&& type_visibility == TypeAttributes::NOT_PUBLIC
{
}
if interface_type.name.is_empty() {
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!(
"Type '{}' implements interface with empty name",
type_entry.name
),
});
}
}
}
if type_entry.flags & TypeAttributes::INTERFACE != 0 {
for (_, field) in type_entry.fields.iter() {
if field.flags & FieldAttributes::STATIC == 0 {
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!(
"Interface '{}' contains non-static field '{}'",
type_entry.name, field.name
),
});
}
if field.flags & 0x0040 == 0 {
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!(
"Interface '{}' contains non-constant field '{}'",
type_entry.name, field.name
),
});
}
}
}
}
Ok(())
}
fn validate_inheritance_accessibility(context: &OwnedValidationContext) {
for type_entry in context.all_types() {
if type_entry.flags & 0x0000_0100 != 0 {
if type_entry.flags & 0x0000_0080 != 0 {
}
}
if type_entry.flags & 0x0000_0080 != 0 {
if type_entry.flags & TypeAttributes::INTERFACE != 0 {
}
}
}
}
}
impl OwnedValidator for OwnedAccessibilityValidator {
fn validate_owned(&self, context: &OwnedValidationContext) -> Result<()> {
self.validate_type_accessibility(context)?;
self.validate_member_accessibility(context)?;
self.validate_interface_accessibility(context)?;
Self::validate_inheritance_accessibility(context);
Ok(())
}
fn name(&self) -> &'static str {
"OwnedAccessibilityValidator"
}
fn priority(&self) -> u32 {
160
}
fn should_run(&self, context: &OwnedValidationContext) -> bool {
context.config().enable_semantic_validation
}
}
impl Default for OwnedAccessibilityValidator {
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_accessibility::owned_accessibility_validator_file_factory,
owned_validator_test,
},
};
#[test]
#[cfg(not(feature = "skip-expensive-tests"))]
fn test_owned_accessibility_validator() -> Result<()> {
let validator = OwnedAccessibilityValidator::new();
let config = ValidationConfig {
enable_semantic_validation: true,
..Default::default()
};
owned_validator_test(
owned_accessibility_validator_file_factory,
"OwnedAccessibilityValidator",
"ValidationOwnedFailed",
config,
|context| validator.validate_owned(context),
)
}
}