show multiple errors and mark tick/load annotation incompatible with parameters
This commit is contained in:
		
							parent
							
								
									eb595bc28b
								
							
						
					
					
						commit
						7e96a43e5f
					
				|  | @ -29,6 +29,8 @@ pub enum Error { | |||
|     InvalidNamespaceName(#[from] InvalidNamespaceName), | ||||
|     #[error(transparent)] | ||||
|     UnresolvedMacroUsage(#[from] UnresolvedMacroUsage), | ||||
|     #[error(transparent)] | ||||
|     IncompatibleFunctionAnnotation(#[from] IncompatibleFunctionAnnotation), | ||||
| } | ||||
| 
 | ||||
| /// An error that occurs when a function declaration is missing.
 | ||||
|  | @ -212,3 +214,30 @@ impl Display for UnresolvedMacroUsage { | |||
| } | ||||
| 
 | ||||
| impl std::error::Error for UnresolvedMacroUsage {} | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||||
| pub struct IncompatibleFunctionAnnotation { | ||||
|     pub span: Span, | ||||
|     pub reason: String, | ||||
| } | ||||
| 
 | ||||
| impl Display for IncompatibleFunctionAnnotation { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         write!( | ||||
|             f, | ||||
|             "{}", | ||||
|             Message::new( | ||||
|                 Severity::Error, | ||||
|                 format!( | ||||
|                     "Annotation `{}` cannot be used here, because {}.", | ||||
|                     self.span.str(), | ||||
|                     self.reason | ||||
|                 ) | ||||
|             ) | ||||
|         )?; | ||||
| 
 | ||||
|         write!(f, "\n{}", SourceCodeDisplay::new(&self.span, None::<u8>)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl std::error::Error for IncompatibleFunctionAnnotation {} | ||||
|  |  | |||
|  | @ -5,7 +5,8 @@ | |||
| use std::collections::HashSet; | ||||
| 
 | ||||
| use error::{ | ||||
|     InvalidNamespaceName, MissingFunctionDeclaration, UnexpectedExpression, UnresolvedMacroUsage, | ||||
|     IncompatibleFunctionAnnotation, InvalidNamespaceName, MissingFunctionDeclaration, | ||||
|     UnexpectedExpression, UnresolvedMacroUsage, | ||||
| }; | ||||
| 
 | ||||
| use crate::{ | ||||
|  | @ -39,14 +40,22 @@ impl ProgramFile { | |||
|     ) -> Result<(), error::Error> { | ||||
|         self.namespace().analyze_semantics(handler)?; | ||||
| 
 | ||||
|         let mut errs = Vec::new(); | ||||
|         let function_names = extract_all_function_names(self.declarations(), handler)?; | ||||
| 
 | ||||
|         for declaration in self.declarations() { | ||||
|             declaration.analyze_semantics(&function_names, handler)?; | ||||
|             if let Err(err) = declaration.analyze_semantics(&function_names, handler) { | ||||
|                 errs.push(err); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #[expect(clippy::option_if_let_else)] | ||||
|         if let Some(err) = errs.first() { | ||||
|             Err(err.clone()) | ||||
|         } else { | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn extract_all_function_names( | ||||
|  | @ -143,11 +152,30 @@ impl Function { | |||
|         function_names: &HashSet<String>, | ||||
|         handler: &impl Handler<base::Error>, | ||||
|     ) -> Result<(), error::Error> { | ||||
|         let macro_names = self | ||||
|             .parameters() | ||||
|         let macro_names = if let Some(parameters) = self.parameters() { | ||||
|             if let Some(incompatible) = self | ||||
|                 .annotations() | ||||
|                 .iter() | ||||
|             .flat_map(|l| l.elements().map(|el| el.span.str().to_string())) | ||||
|             .collect(); | ||||
|                 .find(|a| ["tick", "load"].contains(&a.identifier().span.str())) | ||||
|             { | ||||
|                 let err = | ||||
|                     error::Error::IncompatibleFunctionAnnotation(IncompatibleFunctionAnnotation { | ||||
|                         span: incompatible.identifier().span(), | ||||
|                         reason: | ||||
|                             "functions with the `tick` or `load` annotation cannot have parameters" | ||||
|                                 .to_string(), | ||||
|                     }); | ||||
|                 handler.receive(err.clone()); | ||||
|                 return Err(err); | ||||
|             } | ||||
| 
 | ||||
|             parameters | ||||
|                 .elements() | ||||
|                 .map(|el| el.span.str().to_string()) | ||||
|                 .collect() | ||||
|         } else { | ||||
|             HashSet::new() | ||||
|         }; | ||||
| 
 | ||||
|         self.block() | ||||
|             .analyze_semantics(function_names, ¯o_names, handler) | ||||
|  | @ -162,29 +190,35 @@ impl Block { | |||
|         macro_names: &HashSet<String>, | ||||
|         handler: &impl Handler<base::Error>, | ||||
|     ) -> Result<(), error::Error> { | ||||
|         let mut errs = Vec::new(); | ||||
|         for statement in &self.statements { | ||||
|             match statement { | ||||
|             if let Err(err) = match statement { | ||||
|                 Statement::Block(block) => { | ||||
|                     block.analyze_semantics(function_names, macro_names, handler)?; | ||||
|                     block.analyze_semantics(function_names, macro_names, handler) | ||||
|                 } | ||||
|                 Statement::DocComment(_) | Statement::LiteralCommand(_) => {} | ||||
|                 Statement::DocComment(_) | Statement::LiteralCommand(_) => Ok(()), | ||||
|                 Statement::ExecuteBlock(ex) => { | ||||
|                     ex.analyze_semantics(function_names, macro_names, handler)?; | ||||
|                     ex.analyze_semantics(function_names, macro_names, handler) | ||||
|                 } | ||||
|                 Statement::Grouping(group) => { | ||||
|                     group.analyze_semantics(function_names, macro_names, handler)?; | ||||
|                 } | ||||
|                 Statement::Run(run) => { | ||||
|                     run.analyze_semantics(function_names, macro_names, handler)?; | ||||
|                     group.analyze_semantics(function_names, macro_names, handler) | ||||
|                 } | ||||
|                 Statement::Run(run) => run.analyze_semantics(function_names, macro_names, handler), | ||||
|                 Statement::Semicolon(sem) => { | ||||
|                     sem.analyze_semantics(function_names, macro_names, handler)?; | ||||
|                 } | ||||
|                     sem.analyze_semantics(function_names, macro_names, handler) | ||||
|                 } | ||||
|             } { | ||||
|                 errs.push(err); | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         #[expect(clippy::option_if_let_else)] | ||||
|         if let Some(err) = errs.first() { | ||||
|             Err(err.clone()) | ||||
|         } else { | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ExecuteBlock { | ||||
|  | @ -197,13 +231,27 @@ impl ExecuteBlock { | |||
|     ) -> Result<(), error::Error> { | ||||
|         match self { | ||||
|             Self::HeadTail(head, tail) => { | ||||
|                 head.analyze_semantics(function_names, macro_names, handler)?; | ||||
|                 tail.analyze_semantics(function_names, macro_names, handler) | ||||
|                 let head_res = head.analyze_semantics(function_names, macro_names, handler); | ||||
|                 let tail_res = tail.analyze_semantics(function_names, macro_names, handler); | ||||
| 
 | ||||
|                 if head_res.is_err() { | ||||
|                     head_res | ||||
|                 } else { | ||||
|                     tail_res | ||||
|                 } | ||||
|             } | ||||
|             Self::IfElse(cond, then, el) => { | ||||
|                 cond.analyze_semantics(function_names, macro_names, handler)?; | ||||
|                 then.analyze_semantics(function_names, macro_names, handler)?; | ||||
|                 el.analyze_semantics(function_names, macro_names, handler) | ||||
|                 let cond_res = cond.analyze_semantics(function_names, macro_names, handler); | ||||
|                 let then_res = then.analyze_semantics(function_names, macro_names, handler); | ||||
|                 let else_res = el.analyze_semantics(function_names, macro_names, handler); | ||||
| 
 | ||||
|                 if cond_res.is_err() { | ||||
|                     cond_res | ||||
|                 } else if then_res.is_err() { | ||||
|                     then_res | ||||
|                 } else { | ||||
|                     else_res | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue