slang_frontend/semantic_analysis/
error.rs

1use slang_error::{CompilerError, ErrorCode};
2use slang_ir::Location;
3use slang_shared::CompilationContext;
4use slang_types::TypeId;
5
6/// Represents different categories of semantic analysis errors
7/// that occur during static analysis of the program.
8///
9/// Each variant contains the necessary context for generating
10/// appropriate error messages that maintain the existing format.
11#[derive(Debug, Clone)]
12pub enum SemanticAnalysisError {
13    /// An attempt to use a variable that has not been defined in scope
14    UndefinedVariable {
15        /// The name of the undefined variable
16        name: String,
17        /// The location where the error occurred
18        location: Location,
19    },
20
21    /// A variable with the same name is already defined in the current scope
22    VariableRedefinition {
23        /// The name of the variable being redefined
24        name: String,
25        /// The location where the redefinition occurred
26        location: Location,
27    },
28
29    /// A symbol (type, variable, function) is being redefined.
30    SymbolRedefinition {
31        /// The name of the symbol being redefined
32        name: String,
33        /// The kind of the symbol (e.g., type, variable, function)
34        kind: String,
35        /// The location where the redefinition occurred
36        location: Location,
37    },
38
39    /// A struct field is defined with an invalid type (e.g. unknown, unspecified)
40    InvalidFieldType {
41        /// The name of the struct containing the field
42        struct_name: String,
43        /// The name of the field with the invalid type
44        field_name: String,
45        /// The type ID of the invalid field type
46        type_id: TypeId,
47        /// The location where the invalid field type was defined
48        location: Location,
49    },
50
51    /// The type of an expression does not match the expected type
52    TypeMismatch {
53        /// The expected type
54        expected: TypeId,
55        /// The actual type found
56        actual: TypeId,
57        /// Optional context for the mismatch (like variable or function name)
58        context: Option<String>,
59        /// The location where the type mismatch occurred
60        location: Location,
61    },
62
63    /// Incompatible types for an operation like arithmetic or comparison
64    OperationTypeMismatch {
65        /// The operation being performed (e.g., +, -, *, /)
66        operator: String,
67        /// Left operand type
68        left_type: TypeId,
69        /// Right operand type
70        right_type: TypeId,
71        /// The location where the operation type mismatch occurred
72        location: Location,
73    },
74
75    /// Logical operators (AND, OR) used with non-boolean operands
76    LogicalOperatorTypeMismatch {
77        /// The logical operator being used (AND, OR)
78        operator: String,
79        /// Left operand type
80        left_type: TypeId,
81        /// Right operand type
82        right_type: TypeId,
83        /// The location where the logical operator type mismatch occurred
84        location: Location,
85    },
86
87    /// Value is out of range for the target type (e.g., integer overflow)
88    ValueOutOfRange {
89        /// The value that can't fit in the type
90        value: String,
91        /// The target type
92        target_type: TypeId,
93        /// Whether the value is an integer or float
94        is_float: bool,
95        /// The location where the value out of range occurred
96        location: Location,
97    },
98
99    /// Function call with wrong number of arguments
100    ArgumentCountMismatch {
101        /// Function name
102        function_name: String,
103        /// Expected number of arguments
104        expected: usize,
105        /// Actual number of arguments provided
106        actual: usize,
107        /// The location where the argument count mismatch occurred
108        location: Location,
109    },
110
111    /// Function call with wrong argument types
112    ArgumentTypeMismatch {
113        /// Function name
114        function_name: String,
115        /// Argument position (1-based)
116        argument_position: usize,
117        /// Expected type
118        expected: TypeId,
119        /// Actual type
120        actual: TypeId,
121        /// The location where the argument type mismatch occurred
122        location: Location,
123    },
124
125    /// Return statement outside of a function
126    ReturnOutsideFunction {
127        /// The source location where the return statement was found
128        location: Location,
129    },
130
131    /// Return type does not match function declaration
132    ReturnTypeMismatch {
133        /// Expected return type
134        expected: TypeId,
135        /// Actual returned type
136        actual: TypeId,
137        /// The location where the return type mismatch occurred
138        location: Location,
139    },
140
141    /// Missing return value for a function that requires one
142    MissingReturnValue {
143        /// Expected return type
144        expected: TypeId,
145        /// The location where the missing return value was found
146        location: Location,
147    },
148
149    /// Undefined function in a function call
150    UndefinedFunction {
151        /// The name of the undefined function
152        name: String,
153        /// The location where the undefined function was called
154        location: Location,
155    },
156
157    /// Unary operation applied to incompatible type
158    InvalidUnaryOperation {
159        /// The unary operator (e.g., -, !)
160        operator: String,
161        /// The operand type
162        operand_type: TypeId,
163        /// The location where the invalid unary operation occurred
164        location: Location,
165    },
166
167    /// Assignment to an immutable variable
168    AssignmentToImmutableVariable {
169        /// The name of the immutable variable
170        name: String,
171        /// The location where the assignment attempt occurred
172        location: Location,
173    },
174
175    /// An expression has an unexpected form or context
176    InvalidExpression {
177        /// A description of what was expected vs what was found
178        message: String,
179        /// The location where the invalid expression was found
180        location: Location,
181    },
182
183    /// Attempt to call a variable that is not a function
184    VariableNotCallable {
185        /// The name of the variable being called
186        variable_name: String,
187        /// The actual type of the variable
188        variable_type: TypeId,
189        /// The location where the invalid call was attempted
190        location: Location,
191    },
192}
193
194impl SemanticAnalysisError {
195    /// Convert the SemanticAnalysisError to a String representation
196    /// that matches the existing error message formats.
197    pub fn format_message(&self, context: &CompilationContext) -> String {
198        match self {
199            SemanticAnalysisError::UndefinedVariable { name, .. } => {
200                format!("Undefined variable: {}", name)
201            }
202
203            SemanticAnalysisError::VariableRedefinition { name, .. } => {
204                format!("Variable '{}' already defined", name)
205            }
206
207            SemanticAnalysisError::SymbolRedefinition { name, kind, .. } => match kind.as_str() {
208                "function" => format!(
209                    "Function '{}' is already defined in the current scope.",
210                    name
211                ),
212                "variable" => format!(
213                    "Variable '{}' is already defined in the current scope.",
214                    name
215                ),
216                "type" => format!("Type '{}' is already defined in the current scope.", name),
217                "parameter" => format!(
218                    "Parameter '{}' is already defined in the current scope.",
219                    name
220                ),
221                "variable (conflicts with type)" => format!(
222                    "Symbol '{}' of kind 'variable (conflicts with type)' is already defined or conflicts with an existing symbol.",
223                    name
224                ),
225                "variable (conflicts with function)" => format!(
226                    "Symbol '{}' of kind 'variable (conflicts with function)' is already defined or conflicts with an existing symbol.",
227                    name
228                ),
229                _ => format!("Symbol '{}' is already defined in the current scope.", name),
230            },
231
232            SemanticAnalysisError::InvalidFieldType {
233                struct_name,
234                field_name,
235                type_id,
236                ..
237            } => {
238                format!(
239                    "Invalid type '{}' for field '{}' in struct '{}'. Fields cannot be of unknown or unspecified type.",
240                    context.get_type_name(type_id),
241                    field_name,
242                    struct_name
243                )
244            }
245
246            SemanticAnalysisError::TypeMismatch {
247                expected,
248                actual,
249                context: error_context,
250                ..
251            } => {
252                if let Some(ctx) = error_context {
253                    format!(
254                        "Type mismatch: variable {} is {} but expression is {}",
255                        ctx,
256                        context.get_type_name(expected),
257                        context.get_type_name(actual)
258                    )
259                } else {
260                    format!(
261                        "Type mismatch: expected {}, got {}",
262                        context.get_type_name(expected),
263                        context.get_type_name(actual)
264                    )
265                }
266            }
267
268            SemanticAnalysisError::OperationTypeMismatch {
269                operator,
270                left_type,
271                right_type,
272                ..
273            } => {
274                format!(
275                    "Type mismatch: cannot apply '{}' operator on {} and {}",
276                    operator,
277                    context.get_type_name(left_type),
278                    context.get_type_name(right_type)
279                )
280            }
281
282            SemanticAnalysisError::LogicalOperatorTypeMismatch {
283                operator,
284                left_type,
285                right_type,
286                ..
287            } => {
288                format!(
289                    "Logical operator '{}' requires boolean operands, got {} and {}",
290                    operator,
291                    context.get_type_name(left_type),
292                    context.get_type_name(right_type)
293                )
294            }
295
296            SemanticAnalysisError::ValueOutOfRange {
297                value,
298                target_type,
299                is_float,
300                ..
301            } => {
302                if *is_float {
303                    format!(
304                        "Float literal {} is out of range for type {}",
305                        value,
306                        context.get_type_name(target_type)
307                    )
308                } else {
309                    format!(
310                        "Integer literal {} is out of range for type {}",
311                        value,
312                        context.get_type_name(target_type)
313                    )
314                }
315            }
316
317            SemanticAnalysisError::ArgumentCountMismatch {
318                function_name,
319                expected,
320                actual,
321                ..
322            } => {
323                format!(
324                    "Function '{}' expects {} arguments, but got {}",
325                    function_name, expected, actual
326                )
327            }
328
329            SemanticAnalysisError::ArgumentTypeMismatch {
330                function_name,
331                argument_position,
332                expected,
333                actual,
334                ..
335            } => {
336                format!(
337                    "Type mismatch: function '{}' expects argument {} to be {}, but got {}",
338                    function_name,
339                    argument_position,
340                    context.get_type_name(expected),
341                    context.get_type_name(actual)
342                )
343            }
344
345            SemanticAnalysisError::ReturnOutsideFunction { .. } => {
346                "Return statement outside of function".to_string()
347            }
348
349            SemanticAnalysisError::ReturnTypeMismatch {
350                expected, actual, ..
351            } => {
352                format!(
353                    "Type mismatch: function returns {} but got {}",
354                    context.get_type_name(expected),
355                    context.get_type_name(actual)
356                )
357            }
358
359            SemanticAnalysisError::MissingReturnValue { expected, .. } => {
360                format!(
361                    "Type mismatch: function returns {} but no return value provided",
362                    context.get_type_name(expected)
363                )
364            }
365
366            SemanticAnalysisError::UndefinedFunction { name, .. } => {
367                format!("Undefined function: {}", name)
368            }
369
370            SemanticAnalysisError::InvalidUnaryOperation {
371                operator,
372                operand_type,
373                ..
374            } => {
375                if operator == "!" {
376                    format!(
377                        "Boolean not operator '!' can only be applied to boolean types, but got {}",
378                        context.get_type_name(operand_type)
379                    )
380                } else if operator == "-" {
381                    if context.get_type_name(operand_type) == "u32"
382                        || context.get_type_name(operand_type) == "u64"
383                    {
384                        "Cannot negate unsigned type".to_string()
385                    } else {
386                        format!(
387                            "Cannot negate non-numeric type '{}'",
388                            context.get_type_name(operand_type)
389                        )
390                    }
391                } else {
392                    format!(
393                        "Cannot apply operator '{}' to type {}",
394                        operator,
395                        context.get_type_name(operand_type)
396                    )
397                }
398            }
399
400            SemanticAnalysisError::AssignmentToImmutableVariable { name, .. } => {
401                format!("Cannot assign to immutable variable '{}'", name)
402            }
403
404            SemanticAnalysisError::InvalidExpression { message, .. } => message.clone(),
405
406            SemanticAnalysisError::VariableNotCallable {
407                variable_name,
408                variable_type,
409                ..
410            } => {
411                format!(
412                    "Cannot call {} type '{}' as a function",
413                    context.get_type_name(variable_type),
414                    variable_name
415                )
416            }
417        }
418    }
419
420    /// Extracts the SourceLocation from any SemanticAnalysisError variant.
421    fn get_location(&self) -> &Location {
422        match self {
423            SemanticAnalysisError::UndefinedVariable { location, .. } => location,
424            SemanticAnalysisError::VariableRedefinition { location, .. } => location,
425            SemanticAnalysisError::SymbolRedefinition { location, .. } => location,
426            SemanticAnalysisError::InvalidFieldType { location, .. } => location,
427            SemanticAnalysisError::TypeMismatch { location, .. } => location,
428            SemanticAnalysisError::OperationTypeMismatch { location, .. } => location,
429            SemanticAnalysisError::LogicalOperatorTypeMismatch { location, .. } => location,
430            SemanticAnalysisError::ValueOutOfRange { location, .. } => location,
431            SemanticAnalysisError::ArgumentCountMismatch { location, .. } => location,
432            SemanticAnalysisError::ArgumentTypeMismatch { location, .. } => location,
433            SemanticAnalysisError::ReturnOutsideFunction { location, .. } => location,
434            SemanticAnalysisError::ReturnTypeMismatch { location, .. } => location,
435            SemanticAnalysisError::MissingReturnValue { location, .. } => location,
436            SemanticAnalysisError::UndefinedFunction { location, .. } => location,
437            SemanticAnalysisError::InvalidUnaryOperation { location, .. } => location,
438            SemanticAnalysisError::AssignmentToImmutableVariable { location, .. } => location,
439            SemanticAnalysisError::InvalidExpression { location, .. } => location,
440            SemanticAnalysisError::VariableNotCallable { location, .. } => location,
441        }
442    }
443
444    /// Determines the token length for the error, using the length from SourceLocation.
445    /// Falls back to heuristics only for cases where location information may not be available.
446    fn get_token_length(&self) -> Option<usize> {
447        let location = self.get_location();
448        return Some(location.length);
449    }
450
451    /// Convert a SemanticAnalysisError to a CompilerError that can be used by the rest of the compiler.
452    ///
453    /// ### Arguments
454    /// * `context` - The CompilationContext providing type names and other context for error messages
455    ///
456    /// ### Returns
457    /// A CompilerError with the appropriate message and location information.
458    pub fn to_compiler_error(&self, context: &CompilationContext) -> CompilerError {
459        let location = self.get_location();
460        let token_length = self.get_token_length();
461        CompilerError::new(
462            self.error_code(),
463            self.format_message(context),
464            location.line,
465            location.column,
466            location.position,
467            token_length,
468        )
469    }
470
471    /// Get the appropriate error code for this semantic error
472    pub fn error_code(&self) -> ErrorCode {
473        match self {
474            SemanticAnalysisError::UndefinedVariable { .. } => ErrorCode::UndefinedVariable,
475            SemanticAnalysisError::VariableRedefinition { .. } => ErrorCode::VariableRedefinition,
476            SemanticAnalysisError::SymbolRedefinition { .. } => ErrorCode::SymbolRedefinition,
477            SemanticAnalysisError::InvalidFieldType { .. } => ErrorCode::InvalidFieldType,
478            SemanticAnalysisError::TypeMismatch { .. } => ErrorCode::TypeMismatch,
479            SemanticAnalysisError::OperationTypeMismatch { .. } => ErrorCode::OperationTypeMismatch,
480            SemanticAnalysisError::LogicalOperatorTypeMismatch { .. } => {
481                ErrorCode::LogicalOperatorTypeMismatch
482            }
483            SemanticAnalysisError::ValueOutOfRange { .. } => ErrorCode::ValueOutOfRange,
484            SemanticAnalysisError::ArgumentCountMismatch { .. } => ErrorCode::ArgumentCountMismatch,
485            SemanticAnalysisError::ArgumentTypeMismatch { .. } => ErrorCode::ArgumentTypeMismatch,
486            SemanticAnalysisError::ReturnOutsideFunction { .. } => ErrorCode::ReturnOutsideFunction,
487            SemanticAnalysisError::ReturnTypeMismatch { .. } => ErrorCode::ReturnTypeMismatch,
488            SemanticAnalysisError::MissingReturnValue { .. } => ErrorCode::MissingReturnValue,
489            SemanticAnalysisError::UndefinedFunction { .. } => ErrorCode::UndefinedFunction,
490            SemanticAnalysisError::InvalidUnaryOperation { .. } => ErrorCode::InvalidUnaryOperation,
491            SemanticAnalysisError::AssignmentToImmutableVariable { .. } => {
492                ErrorCode::AssignmentToImmutableVariable
493            }
494            SemanticAnalysisError::InvalidExpression { .. } => ErrorCode::InvalidExpression,
495            SemanticAnalysisError::VariableNotCallable { .. } => ErrorCode::VariableNotCallable,
496        }
497    }
498}