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.visibility();
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.token.table() == 0x02 && type_entry.enclosing_type.get().is_some() {
if !matches!(
visibility,
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!(
"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.is_interface() && type_entry.flags.is_sealed() {
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.visibility();
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.access();
if field_access == FieldAttributes::PUBLIC
&& type_visibility == TypeAttributes::NOT_PUBLIC
{
}
if field.flags.is_literal() && !field.flags.is_static() {
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 (_, entry) in type_entry.interfaces.iter() {
let type_visibility = type_entry.flags.visibility();
if let Some(interface_type) = entry.interface.upgrade() {
let interface_visibility = interface_type.flags.visibility();
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.is_interface() {
for (_, field) in type_entry.fields.iter() {
if !field.flags.is_static() {
return Err(Error::ValidationOwnedFailed {
validator: self.name().to_string(),
message: format!(
"Interface '{}' contains non-static field '{}'",
type_entry.name, field.name
),
});
}
if !field.flags.is_literal() {
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.is_sealed() && type_entry.flags.is_abstract() {
}
if type_entry.flags.is_abstract() && type_entry.flags.is_interface() {
}
}
}
}
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),
)
}
}