use thiserror::Error;
use crate::report_code::ReportCode;
use crate::report::Report;
use crate::file_definition::{FileID, FileLocation};
use crate::ir::errors::IRError;
#[derive(Debug, Error)]
pub enum CFGError {
#[error("The variable `{name}` is read before it is declared/written.")]
UndefinedVariableError { name: String, file_id: Option<FileID>, file_location: FileLocation },
#[error("The variable name `{name}` contains invalid characters.")]
InvalidVariableNameError { name: String, file_id: Option<FileID>, file_location: FileLocation },
#[error("The declaration of the variable `{name}` shadows a previous declaration.")]
ShadowingVariableWarning {
name: String,
primary_file_id: Option<FileID>,
primary_location: FileLocation,
secondary_file_id: Option<FileID>,
secondary_location: FileLocation,
},
#[error("Multiple parameters with the same name `{name}` in function or template definition.")]
ParameterNameCollisionError {
name: String,
file_id: Option<FileID>,
file_location: FileLocation,
},
}
pub type CFGResult<T> = Result<T, CFGError>;
impl CFGError {
pub fn into_report(self) -> Report {
use CFGError::*;
match self {
UndefinedVariableError { name, file_id, file_location } => {
let mut report = Report::error(
format!("The variable `{name}` is used before it is defined."),
ReportCode::UninitializedSymbolInExpression,
);
if let Some(file_id) = file_id {
report.add_primary(
file_location,
file_id,
format!("The variable `{name}` is first seen here."),
);
}
report
}
InvalidVariableNameError { name, file_id, file_location } => {
let mut report = Report::error(
format!("Invalid variable name `{name}`."),
ReportCode::ParseFail,
);
if let Some(file_id) = file_id {
report.add_primary(
file_location,
file_id,
"This variable name contains invalid characters.".to_string(),
);
}
report
}
ShadowingVariableWarning {
name,
primary_file_id,
primary_location,
secondary_file_id,
secondary_location,
} => {
let mut report = Report::warning(
format!("Declaration of variable `{name}` shadows previous declaration."),
ReportCode::ShadowingVariable,
);
if let Some(primary_file_id) = primary_file_id {
report.add_primary(
primary_location,
primary_file_id,
"Shadowing declaration here.".to_string(),
);
}
if let Some(secondary_file_id) = secondary_file_id {
report.add_secondary(
secondary_location,
secondary_file_id,
Some("Shadowed variable is declared here.".to_string()),
);
}
report.add_note(format!("Consider renaming the second occurrence of `{name}`."));
report
}
ParameterNameCollisionError { name, file_id, file_location } => {
let mut report = Report::warning(
format!("Parameter `{name}` declared multiple times."),
ReportCode::ParameterNameCollision,
);
if let Some(file_id) = file_id {
report.add_primary(
file_location,
file_id,
"Parameters declared here.".to_string(),
);
}
report.add_note(format!("Rename the second occurrence of `{name}`."));
report
}
}
}
}
impl From<IRError> for CFGError {
fn from(error: IRError) -> CFGError {
match error {
IRError::UndefinedVariableError { name, file_id, file_location } => {
CFGError::UndefinedVariableError { name, file_id, file_location }
}
IRError::InvalidVariableNameError { name, file_id, file_location } => {
CFGError::InvalidVariableNameError { name, file_id, file_location }
}
}
}
}
impl From<CFGError> for Report {
fn from(error: CFGError) -> Report {
error.into_report()
}
}