use std::path::Path;
use std::sync::Arc;
use crate::check_config::CheckConfig;
use crate::ir::{FileIr, TypeDefKind};
use crate::violation::{Violation, ViolationType};
use super::common::emit_violation;
pub(super) fn check_module_root_definitions(
ir: &FileIr,
config: &CheckConfig,
fp: &Arc<str>,
violations: &mut Vec<Violation>,
) {
if !config.check_module_root_definitions || !is_module_root(fp, config) {
return;
}
for def in &ir.type_defs {
emit(violations, fp, def.span, type_keyword(def.kind), &def.name);
}
for imp in &ir.impl_blocks {
emit(violations, fp, imp.span, "impl", &imp.self_type);
}
for func in &ir.functions {
if !func.is_associated {
emit(violations, fp, func.span, "fn", &func.name);
}
}
}
fn is_module_root(fp: &str, config: &CheckConfig) -> bool {
let Some(name) = Path::new(fp).file_name().and_then(|n| n.to_str()) else {
return false;
};
config.module_root_files.iter().any(|root| &**root == name)
}
fn type_keyword(kind: TypeDefKind) -> &'static str {
match kind {
TypeDefKind::Struct => "struct",
TypeDefKind::Enum => "enum",
TypeDefKind::Trait => "trait",
TypeDefKind::Union => "union",
}
}
fn emit(
violations: &mut Vec<Violation>,
fp: &Arc<str>,
span: crate::ir::IrSpan,
keyword: &str,
name: &str,
) {
emit_violation(
violations,
fp,
span,
ViolationType::ModuleRootDefinitions,
format!(
"module root defines `{keyword} {name}`; move it to a leaf module and re-export with `pub use`"
),
);
}