From 218f488e76e49750f52d37f03c104f7ecd444ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Sun, 7 Sep 2025 12:58:35 +0200 Subject: [PATCH] list alternative identifiers using oxford_join --- Cargo.toml | 1 + src/semantic/error.rs | 7 +-- src/transpile/error.rs | 119 ++++---------------------------------- src/transpile/function.rs | 19 +++--- 4 files changed, 20 insertions(+), 126 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4275e2b..342c10d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ enum-as-inner = "0.6.0" getset = "0.1.2" itertools = "0.14.0" mlua = { version = "0.11.3", features = ["lua54", "vendored"], optional = true } +oxford_join = { version = "0.7.1", optional = true } pathdiff = "0.2.3" serde = { version = "1.0.217", features = ["derive"], optional = true } serde_json = { version = "1.0.138", optional = true } diff --git a/src/semantic/error.rs b/src/semantic/error.rs index ee58aa8..962f916 100644 --- a/src/semantic/error.rs +++ b/src/semantic/error.rs @@ -11,17 +11,12 @@ use crate::{ }, lexical::token::StringLiteral, syntax::syntax_tree::expression::Expression, - transpile::error::{ - AssignmentError, IllegalIndexing, MismatchedTypes, MissingFunctionDeclaration, - UnknownIdentifier, - }, + transpile::error::{AssignmentError, IllegalIndexing, MismatchedTypes, UnknownIdentifier}, }; #[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)] #[allow(missing_docs)] pub enum Error { - #[error(transparent)] - MissingFunctionDeclaration(#[from] MissingFunctionDeclaration), #[error(transparent)] UnexpectedExpression(#[from] UnexpectedExpression), #[error(transparent)] diff --git a/src/transpile/error.rs b/src/transpile/error.rs index 3d6f402..b350084 100644 --- a/src/transpile/error.rs +++ b/src/transpile/error.rs @@ -3,6 +3,7 @@ use std::fmt::Display; use getset::Getters; +use oxford_join::OxfordJoin as _; use crate::{ base::{ @@ -12,14 +13,12 @@ use crate::{ semantic::error::{ConflictingFunctionNames, InvalidFunctionArguments, UnexpectedExpression}, }; -use super::{expression::ExpectedType, FunctionData}; +use super::expression::ExpectedType; /// Errors that can occur during transpilation. #[allow(clippy::module_name_repetitions, missing_docs)] #[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)] pub enum TranspileError { - #[error(transparent)] - MissingFunctionDeclaration(#[from] MissingFunctionDeclaration), #[error(transparent)] UnexpectedExpression(#[from] UnexpectedExpression), #[error("Lua code evaluation is disabled.")] @@ -53,101 +52,6 @@ pub enum TranspileError { /// The result of a transpilation operation. pub type TranspileResult = Result; -/// An error that occurs when a function declaration is missing. -#[derive(Debug, Clone, PartialEq, Eq, Getters)] -pub struct MissingFunctionDeclaration { - /// The span of the identifier that is missing. - #[get = "pub"] - span: Span, - /// Possible alternatives for the missing function declaration. - #[get = "pub"] - alternatives: Vec, -} - -impl MissingFunctionDeclaration { - #[cfg(feature = "shulkerbox")] - pub(super) fn from_scope( - identifier_span: Span, - scope: &std::sync::Arc, - ) -> Self { - use itertools::Itertools as _; - - let own_name = identifier_span.str(); - let alternatives = scope - .get_all_variables() - .iter() - .filter_map(|(name, value)| { - let super::variables::VariableData::Function { - function_data: data, - .. - } = value.as_ref() - else { - return None; - }; - - let normalized_distance = strsim::normalized_damerau_levenshtein(own_name, name); - (normalized_distance > 0.8 || strsim::damerau_levenshtein(own_name, name) < 3) - .then_some((normalized_distance, data)) - }) - .sorted_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal)) - .map(|(_, data)| data) - .take(8) - .cloned() - .collect::>(); - - Self { - alternatives, - span: identifier_span, - } - } -} - -impl Display for MissingFunctionDeclaration { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use std::fmt::Write as _; - - let message = format!( - "no matching function declaration found for invocation of function `{}`", - self.span.str() - ); - write!(f, "{}", Message::new(Severity::Error, message))?; - - let help_message = if self.alternatives.is_empty() { - None - } else { - let mut message = String::from("did you mean "); - for (i, alternative) in self.alternatives.iter().enumerate() { - if i > 0 { - message.push_str(", "); - } - let _ = write!(message, "`{}`", alternative.identifier_span.str()); - } - Some(message + "?") - }; - - write!( - f, - "\n{}", - SourceCodeDisplay::new(&self.span, help_message.as_ref()) - ) - } -} - -impl std::error::Error for MissingFunctionDeclaration {} - -impl std::hash::Hash for MissingFunctionDeclaration { - fn hash(&self, state: &mut H) { - self.span.hash(state); - for alternative in &self.alternatives { - alternative.identifier_span.hash(state); - alternative.namespace.hash(state); - alternative.parameters.hash(state); - alternative.public.hash(state); - alternative.statements.hash(state); - } - } -} - /// An error that occurs when a function declaration is missing. #[allow(clippy::module_name_repetitions)] #[derive(Debug, Clone, PartialEq, Eq)] @@ -341,8 +245,6 @@ impl UnknownIdentifier { impl Display for UnknownIdentifier { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use std::fmt::Write as _; - write!( f, "{}", @@ -355,14 +257,15 @@ impl Display for UnknownIdentifier { let help_message = if self.alternatives.is_empty() { None } else { - let mut message = String::from("did you mean "); - for (i, alternative) in self.alternatives.iter().enumerate() { - if i > 0 { - message.push_str(", "); - } - let _ = write!(message, "`{alternative}`"); - } - Some(message + "?") + let message = String::from("did you mean "); + let inner = self + .alternatives + .iter() + .map(|s| format!("`{s}`")) + .collect::>(); + let inner = inner.oxford_or(); + + Some(message + &inner + "?") }; write!( diff --git a/src/transpile/function.rs b/src/transpile/function.rs index b2422b5..ad45de3 100644 --- a/src/transpile/function.rs +++ b/src/transpile/function.rs @@ -22,7 +22,7 @@ use crate::{ statement::Statement, }, transpile::{ - error::{IllegalAnnotationContent, MissingFunctionDeclaration}, + error::IllegalAnnotationContent, util::{MacroString, MacroStringPart}, variables::FunctionVariableDataType, }, @@ -57,9 +57,10 @@ impl Transpiler { let function = scope.get_variable(identifier_span.str()); let function_data = function.ok_or_else(|| { - let err = TranspileError::MissingFunctionDeclaration( - MissingFunctionDeclaration::from_scope(identifier_span.clone(), scope), - ); + let err = TranspileError::UnknownIdentifier(UnknownIdentifier::from_scope( + identifier_span.clone(), + scope, + )); handler.receive(Box::new(err.clone())); err })?; @@ -91,14 +92,8 @@ impl Transpiler { let function_location = function_path .get() - .ok_or_else(|| { - let err = TranspileError::MissingFunctionDeclaration( - MissingFunctionDeclaration::from_scope(identifier_span.clone(), scope), - ); - handler.receive(Box::new(err.clone())); - err - }) - .map(String::to_owned)?; + .expect("set in prepare_transpile_function if not previously set") + .to_string(); let args = self.transpile_function_arguments( function_data,