use strsim to suggest similar identifiers in UnknownIdentifier error
This commit is contained in:
		
							parent
							
								
									d9f2d99c3a
								
							
						
					
					
						commit
						ef7bf95447
					
				| 
						 | 
				
			
			@ -456,9 +456,10 @@ impl Assignment {
 | 
			
		|||
                return Err(err);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            let err = error::Error::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                identifier: self.destination().span(),
 | 
			
		||||
            });
 | 
			
		||||
            let err = error::Error::UnknownIdentifier(UnknownIdentifier::from_semantic_scope(
 | 
			
		||||
                self.destination().span(),
 | 
			
		||||
                scope,
 | 
			
		||||
            ));
 | 
			
		||||
            handler.receive(err.clone());
 | 
			
		||||
            return Err(err);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -574,9 +575,9 @@ impl Primary {
 | 
			
		|||
        match self {
 | 
			
		||||
            Self::Identifier(ident) => {
 | 
			
		||||
                if scope.get_variable(ident.span.str()).is_none() {
 | 
			
		||||
                    let err = error::Error::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                        identifier: ident.span.clone(),
 | 
			
		||||
                    });
 | 
			
		||||
                    let err = error::Error::UnknownIdentifier(
 | 
			
		||||
                        UnknownIdentifier::from_semantic_scope(ident.span.clone(), scope),
 | 
			
		||||
                    );
 | 
			
		||||
                    handler.receive(err.clone());
 | 
			
		||||
                    Err(err)
 | 
			
		||||
                } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -589,9 +590,12 @@ impl Primary {
 | 
			
		|||
                let var = scope.get_variable(call.identifier().span.str());
 | 
			
		||||
                var.map_or_else(
 | 
			
		||||
                    || {
 | 
			
		||||
                        let err = error::Error::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                            identifier: call.identifier().span.clone(),
 | 
			
		||||
                        });
 | 
			
		||||
                        let err = error::Error::UnknownIdentifier(
 | 
			
		||||
                            UnknownIdentifier::from_semantic_scope(
 | 
			
		||||
                                call.identifier().span.clone(),
 | 
			
		||||
                                scope,
 | 
			
		||||
                            ),
 | 
			
		||||
                        );
 | 
			
		||||
                        handler.receive(err.clone());
 | 
			
		||||
                        Err(err)
 | 
			
		||||
                    },
 | 
			
		||||
| 
						 | 
				
			
			@ -661,8 +665,8 @@ impl Primary {
 | 
			
		|||
                    Err(err)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Self::MemberAccess(_) => {
 | 
			
		||||
                // TODO:
 | 
			
		||||
            Self::MemberAccess(member_access) => {
 | 
			
		||||
                member_access.parent().analyze_semantics(scope, handler)?;
 | 
			
		||||
                Ok(())
 | 
			
		||||
            }
 | 
			
		||||
            Self::Parenthesized(expr) => expr.analyze_semantics(scope, handler),
 | 
			
		||||
| 
						 | 
				
			
			@ -971,9 +975,12 @@ impl TemplateStringLiteral {
 | 
			
		|||
                    match expression.as_ref() {
 | 
			
		||||
                        Expression::Primary(Primary::Identifier(identifier)) => {
 | 
			
		||||
                            if scope.get_variable(identifier.span.str()).is_none() {
 | 
			
		||||
                                let err = error::Error::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                                    identifier: identifier.span.clone(),
 | 
			
		||||
                                });
 | 
			
		||||
                                let err = error::Error::UnknownIdentifier(
 | 
			
		||||
                                    UnknownIdentifier::from_semantic_scope(
 | 
			
		||||
                                        identifier.span.clone(),
 | 
			
		||||
                                        scope,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                );
 | 
			
		||||
                                handler.receive(err.clone());
 | 
			
		||||
                                errs.push(err);
 | 
			
		||||
                            }
 | 
			
		||||
| 
						 | 
				
			
			@ -999,9 +1006,12 @@ impl TemplateStringLiteral {
 | 
			
		|||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    let err = error::Error::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                                        identifier: identifier.span.clone(),
 | 
			
		||||
                                    });
 | 
			
		||||
                                    let err = error::Error::UnknownIdentifier(
 | 
			
		||||
                                        UnknownIdentifier::from_semantic_scope(
 | 
			
		||||
                                            identifier.span.clone(),
 | 
			
		||||
                                            scope,
 | 
			
		||||
                                        ),
 | 
			
		||||
                                    );
 | 
			
		||||
                                    handler.receive(err.clone());
 | 
			
		||||
                                    errs.push(err);
 | 
			
		||||
                                }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
use std::{collections::HashMap, sync::RwLock};
 | 
			
		||||
 | 
			
		||||
use crate::{base::source_file::Span, transpile::error::UnknownIdentifier};
 | 
			
		||||
 | 
			
		||||
/// Type of variable
 | 
			
		||||
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
 | 
			
		||||
pub enum VariableType {
 | 
			
		||||
| 
						 | 
				
			
			@ -95,3 +97,29 @@ impl<'a> SemanticScope<'a> {
 | 
			
		|||
        self.parent
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl UnknownIdentifier {
 | 
			
		||||
    pub(super) fn from_semantic_scope(identifier: Span, scope: &SemanticScope<'_>) -> Self {
 | 
			
		||||
        use itertools::Itertools as _;
 | 
			
		||||
 | 
			
		||||
        let own_name = identifier.str();
 | 
			
		||||
        let alternatives = scope
 | 
			
		||||
            .get_all_variables()
 | 
			
		||||
            .iter()
 | 
			
		||||
            .filter_map(|(name, _)| {
 | 
			
		||||
                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, name))
 | 
			
		||||
            })
 | 
			
		||||
            .sorted_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal))
 | 
			
		||||
            .map(|(_, data)| data)
 | 
			
		||||
            .take(8)
 | 
			
		||||
            .cloned()
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            identifier,
 | 
			
		||||
            alternatives,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -303,11 +303,46 @@ impl std::error::Error for AssignmentError {}
 | 
			
		|||
pub struct UnknownIdentifier {
 | 
			
		||||
    /// The unknown identifier.
 | 
			
		||||
    #[get = "pub"]
 | 
			
		||||
    pub identifier: Span,
 | 
			
		||||
    pub(crate) identifier: Span,
 | 
			
		||||
    /// Alternatives to the current identifier
 | 
			
		||||
    #[get = "pub"]
 | 
			
		||||
    pub(crate) alternatives: Vec<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl UnknownIdentifier {
 | 
			
		||||
    #[cfg(feature = "shulkerbox")]
 | 
			
		||||
    pub(crate) fn from_scope(
 | 
			
		||||
        identifier: Span,
 | 
			
		||||
        scope: &std::sync::Arc<super::variables::Scope>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        use itertools::Itertools as _;
 | 
			
		||||
 | 
			
		||||
        let own_name = identifier.str();
 | 
			
		||||
        let alternatives = scope
 | 
			
		||||
            .get_all_variables()
 | 
			
		||||
            .iter()
 | 
			
		||||
            .filter_map(|(name, _)| {
 | 
			
		||||
                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, name))
 | 
			
		||||
            })
 | 
			
		||||
            .sorted_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal))
 | 
			
		||||
            .map(|(_, data)| data)
 | 
			
		||||
            .take(8)
 | 
			
		||||
            .cloned()
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            identifier,
 | 
			
		||||
            alternatives,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display for UnknownIdentifier {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        use std::fmt::Write as _;
 | 
			
		||||
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "{}",
 | 
			
		||||
| 
						 | 
				
			
			@ -317,10 +352,23 @@ 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 + "?")
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "\n{}",
 | 
			
		||||
            SourceCodeDisplay::new(&self.identifier, Option::<u8>::None)
 | 
			
		||||
            SourceCodeDisplay::new(&self.identifier, help_message.as_ref())
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1460,9 +1460,10 @@ impl Transpiler {
 | 
			
		|||
 | 
			
		||||
                    self.move_data(&from, target, primary, handler)
 | 
			
		||||
                } else {
 | 
			
		||||
                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                        identifier: ident.span.clone(),
 | 
			
		||||
                    });
 | 
			
		||||
                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier::from_scope(
 | 
			
		||||
                        ident.span.clone(),
 | 
			
		||||
                        scope,
 | 
			
		||||
                    ));
 | 
			
		||||
                    handler.receive(Box::new(err.clone()));
 | 
			
		||||
                    Err(err)
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -1587,9 +1588,10 @@ impl Transpiler {
 | 
			
		|||
 | 
			
		||||
                    self.move_data(&from, target, primary, handler)
 | 
			
		||||
                } else {
 | 
			
		||||
                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                        identifier: ident.span.clone(),
 | 
			
		||||
                    });
 | 
			
		||||
                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier::from_scope(
 | 
			
		||||
                        ident.span.clone(),
 | 
			
		||||
                        scope,
 | 
			
		||||
                    ));
 | 
			
		||||
                    handler.receive(Box::new(err.clone()));
 | 
			
		||||
                    Err(err)
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -1740,9 +1742,10 @@ impl Transpiler {
 | 
			
		|||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                        identifier: ident.span.clone(),
 | 
			
		||||
                    });
 | 
			
		||||
                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier::from_scope(
 | 
			
		||||
                        ident.span.clone(),
 | 
			
		||||
                        scope,
 | 
			
		||||
                    ));
 | 
			
		||||
                    handler.receive(Box::new(err.clone()));
 | 
			
		||||
                    Err(err)
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -1816,9 +1819,10 @@ impl Transpiler {
 | 
			
		|||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                        identifier: ident.span.clone(),
 | 
			
		||||
                    });
 | 
			
		||||
                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier::from_scope(
 | 
			
		||||
                        ident.span.clone(),
 | 
			
		||||
                        scope,
 | 
			
		||||
                    ));
 | 
			
		||||
                    handler.receive(Box::new(err.clone()));
 | 
			
		||||
                    Err(err)
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -455,9 +455,7 @@ impl Transpiler {
 | 
			
		|||
                                let var =
 | 
			
		||||
                                    scope.get_variable(ident.span.str()).ok_or_else(|| {
 | 
			
		||||
                                        let err =
 | 
			
		||||
                                            TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                                                identifier: ident.span(),
 | 
			
		||||
                                            });
 | 
			
		||||
                                            TranspileError::UnknownIdentifier(UnknownIdentifier::from_scope(ident.span(), scope));
 | 
			
		||||
                                        handler.receive(Box::new(err.clone()));
 | 
			
		||||
                                        err
 | 
			
		||||
                                    })?;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -156,9 +156,9 @@ fn print_function(
 | 
			
		|||
                ))),
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                identifier: ident.span(),
 | 
			
		||||
            }))
 | 
			
		||||
            Err(TranspileError::UnknownIdentifier(
 | 
			
		||||
                UnknownIdentifier::from_scope(ident.span(), scope),
 | 
			
		||||
            ))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -289,9 +289,9 @@ fn print_function(
 | 
			
		|||
                ))),
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                identifier: ident.span(),
 | 
			
		||||
            }))
 | 
			
		||||
            Err(TranspileError::UnknownIdentifier(
 | 
			
		||||
                UnknownIdentifier::from_scope(ident.span(), scope),
 | 
			
		||||
            ))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -295,9 +295,9 @@ mod enabled {
 | 
			
		|||
                    }));
 | 
			
		||||
                }
 | 
			
		||||
                None => {
 | 
			
		||||
                    return Err(TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                        identifier: identifier.span(),
 | 
			
		||||
                    }));
 | 
			
		||||
                    return Err(TranspileError::UnknownIdentifier(
 | 
			
		||||
                        UnknownIdentifier::from_scope(identifier.span(), scope),
 | 
			
		||||
                    ));
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -552,9 +552,10 @@ impl Transpiler {
 | 
			
		|||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        let err = TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                            identifier: ident.span.clone(),
 | 
			
		||||
                        });
 | 
			
		||||
                        let err = TranspileError::UnknownIdentifier(UnknownIdentifier::from_scope(
 | 
			
		||||
                            ident.span.clone(),
 | 
			
		||||
                            scope,
 | 
			
		||||
                        ));
 | 
			
		||||
                        handler.receive(Box::new(err.clone()));
 | 
			
		||||
                        return Err(err);
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -623,9 +624,10 @@ impl Transpiler {
 | 
			
		|||
                    Err(err)
 | 
			
		||||
                }
 | 
			
		||||
                None => {
 | 
			
		||||
                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                        identifier: ident.span.clone(),
 | 
			
		||||
                    });
 | 
			
		||||
                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier::from_scope(
 | 
			
		||||
                        ident.span.clone(),
 | 
			
		||||
                        scope,
 | 
			
		||||
                    ));
 | 
			
		||||
                    handler.receive(Box::new(err.clone()));
 | 
			
		||||
                    Err(err)
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -326,9 +326,7 @@ impl TemplateStringLiteral {
 | 
			
		|||
                                        }
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        let err =
 | 
			
		||||
                                            TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
			
		||||
                                                identifier: identifier.span(),
 | 
			
		||||
                                            });
 | 
			
		||||
                                            TranspileError::UnknownIdentifier(UnknownIdentifier::from_scope(identifier.span(), scope));
 | 
			
		||||
                                        handler.receive(Box::new(err.clone()));
 | 
			
		||||
                                        Err(err)
 | 
			
		||||
                                    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue