use std::collections::HashSet;
use crate::ir::ast::{ClassDefinition, ClassType, Expression, Variability};
use crate::lint::{LintLevel, LintMessage, LintResult};
pub fn lint_missing_documentation(
_class: &ClassDefinition,
_file_path: &str,
_result: &mut LintResult,
) {
}
pub fn lint_parameter_defaults(class: &ClassDefinition, file_path: &str, result: &mut LintResult) {
for (name, comp) in &class.components {
if matches!(comp.variability, Variability::Parameter(_)) {
let has_default = !matches!(comp.start, Expression::Empty);
if !has_default {
let line = comp
.type_name
.name
.first()
.map(|t| t.location.start_line)
.unwrap_or(1);
let col = comp
.type_name
.name
.first()
.map(|t| t.location.start_column)
.unwrap_or(1);
result.messages.push(
LintMessage::new(
"parameter-no-default",
LintLevel::Help,
format!("Parameter '{}' has no default value", name),
file_path,
line,
col,
)
.with_suggestion("Consider adding a default value for better usability"),
);
}
}
}
}
pub fn lint_empty_sections(class: &ClassDefinition, file_path: &str, result: &mut LintResult) {
let line = class.name.location.start_line;
if matches!(class.class_type, ClassType::Model | ClassType::Block)
&& class.equations.is_empty()
&& class.initial_equations.is_empty()
&& class.algorithms.is_empty()
&& class.initial_algorithms.is_empty()
&& class.extends.is_empty() && !class.components.is_empty()
{
result.messages.push(LintMessage::new(
"empty-section",
LintLevel::Note,
format!(
"{} '{}' has components but no equations or algorithms",
format_class_type(&class.class_type),
class.name.text
),
file_path,
line,
1,
));
}
}
pub fn lint_unit_consistency(class: &ClassDefinition, file_path: &str, result: &mut LintResult) {
let mut has_units = false;
let mut missing_units = Vec::new();
for (name, comp) in &class.components {
if comp.type_name.to_string() != "Real" {
continue;
}
let has_unit = false;
if has_unit {
has_units = true;
} else {
missing_units.push((
name.clone(),
comp.type_name
.name
.first()
.map(|t| t.location.start_line)
.unwrap_or(1),
));
}
}
if has_units && !missing_units.is_empty() {
for (name, line) in missing_units {
result.messages.push(LintMessage::new(
"inconsistent-units",
LintLevel::Warning,
format!(
"Variable '{}' has no unit specification while others do",
name
),
file_path,
line,
1,
));
}
}
}
pub fn lint_redundant_extends(class: &ClassDefinition, file_path: &str, result: &mut LintResult) {
let line = class.name.location.start_line;
let mut seen_extends: HashSet<String> = HashSet::new();
for ext in &class.extends {
let ext_name = ext.comp.to_string();
if seen_extends.contains(&ext_name) {
result.messages.push(LintMessage::new(
"redundant-extends",
LintLevel::Warning,
format!("Duplicate extends clause for '{}'", ext_name),
file_path,
line,
1,
));
}
seen_extends.insert(ext_name);
}
for ext in &class.extends {
if ext.comp.to_string() == class.name.text {
result.messages.push(LintMessage::new(
"redundant-extends",
LintLevel::Error,
format!("Class '{}' extends itself", class.name.text),
file_path,
line,
1,
));
}
}
}
fn format_class_type(ct: &ClassType) -> &'static str {
match ct {
ClassType::Model => "Model",
ClassType::Class => "Class",
ClassType::Block => "Block",
ClassType::Connector => "Connector",
ClassType::Record => "Record",
ClassType::Type => "Type",
ClassType::Function => "Function",
ClassType::Package => "Package",
ClassType::Operator => "Operator",
}
}