-
Notifications
You must be signed in to change notification settings - Fork 0
fix: Return explicit error when dependent rule is not found during deserialization #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
37e86fc
edc574e
3504b87
3620b09
a79e527
64f71ee
7e04537
71a2b4e
37e8317
d8783ba
c148107
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,7 +38,7 @@ fn into_map<L: Language>( | |
| .collect() | ||
| } | ||
|
|
||
| type OrderResult<T> = Result<T, String>; | ||
| type OrderResult<T> = Result<T, ReferentRuleError>; | ||
|
|
||
| /// A struct to store information to deserialize rules. | ||
| #[derive(Clone, Debug)] | ||
|
|
@@ -79,22 +79,24 @@ struct TopologicalSort<'a, T: DependentRule> { | |
| order: Vec<&'a str>, | ||
| // bool stands for if the rule has completed visit | ||
| seen: RapidMap<&'a str, bool>, | ||
| env: Option<&'a RuleRegistration>, | ||
| } | ||
|
|
||
| impl<'a, T: DependentRule> TopologicalSort<'a, T> { | ||
| fn get_order(maps: &RapidMap<String, T>) -> OrderResult<Vec<&str>> { | ||
| let mut top_sort = TopologicalSort::new(maps); | ||
| fn get_order(maps: &'a RapidMap<String, T>, env: Option<&'a RuleRegistration>) -> OrderResult<Vec<&'a str>> { | ||
| let mut top_sort = TopologicalSort::new(maps, env); | ||
| for key in maps.keys() { | ||
| top_sort.visit(key)?; | ||
| } | ||
| Ok(top_sort.order) | ||
| } | ||
|
|
||
| fn new(maps: &'a RapidMap<String, T>) -> Self { | ||
| fn new(maps: &'a RapidMap<String, T>, env: Option<&'a RuleRegistration>) -> Self { | ||
| Self { | ||
| maps, | ||
| order: vec![], | ||
| seen: RapidMap::default(), | ||
| env, | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -105,15 +107,20 @@ impl<'a, T: DependentRule> TopologicalSort<'a, T> { | |
| return if completed { | ||
| Ok(()) | ||
| } else { | ||
| Err(key.to_string()) | ||
| Err(ReferentRuleError::CyclicRule(key.to_string())) | ||
| }; | ||
| } | ||
| let Some(item) = self.maps.get(key) else { | ||
| // key can be found elsewhere | ||
| // e.g. if key is rule_id | ||
| // if rule_id not found in global, it can be a local rule | ||
| // if rule_id not found in local, it can be a global rule | ||
| // TODO: add check here and return Err if rule not found | ||
| if let Some(env) = self.env { | ||
| // Note: We only check if the key is completely missing | ||
| if !env.contains_rule(key) { | ||
| return Err(ReferentRuleError::UndefinedUtil(key.to_string())); | ||
| } | ||
| } | ||
| return Ok(()); | ||
|
Comment on lines
116
to
127
|
||
| }; | ||
| // mark the id as seen but not completed | ||
|
|
@@ -165,8 +172,7 @@ impl<L: Language> DeserializeEnv<L> { | |
| self, | ||
| utils: &RapidMap<String, SerializableRule>, | ||
| ) -> Result<Self, RuleSerializeError> { | ||
| let order = TopologicalSort::get_order(utils) | ||
| .map_err(ReferentRuleError::CyclicRule) | ||
| let order = TopologicalSort::get_order(utils, Some(&self.registration)) | ||
| .map_err(RuleSerializeError::MatchesReference)?; | ||
| for id in order { | ||
| let rule = utils.get(id).expect("must exist"); | ||
|
|
@@ -182,8 +188,7 @@ impl<L: Language> DeserializeEnv<L> { | |
| ) -> Result<GlobalRules, RuleCoreError> { | ||
| let registration = GlobalRules::default(); | ||
| let utils = into_map(utils); | ||
| let order = TopologicalSort::get_order(&utils) | ||
| .map_err(ReferentRuleError::CyclicRule) | ||
| let order = TopologicalSort::get_order(&utils, None) | ||
| .map_err(RuleSerializeError::from)?; | ||
|
Comment on lines
-189
to
196
|
||
| for id in order { | ||
| let (lang, core) = utils.get(id).expect("must exist"); | ||
|
|
@@ -204,10 +209,11 @@ impl<L: Language> DeserializeEnv<L> { | |
| } | ||
|
|
||
| pub(crate) fn get_transform_order<'a>( | ||
| &self, | ||
| &'a self, | ||
| trans: &'a RapidMap<String, Trans<MetaVariable>>, | ||
| ) -> Result<Vec<&'a str>, String> { | ||
| TopologicalSort::get_order(trans) | ||
| ) -> Result<Vec<&'a str>, ReferentRuleError> { | ||
| // Transformations don't need env rule registration checks, pass None | ||
| TopologicalSort::get_order(trans, None) | ||
| } | ||
|
|
||
| pub fn with_globals(self, globals: &GlobalRules) -> Self { | ||
|
|
@@ -277,7 +283,11 @@ local-rule: | |
| ) | ||
| .expect("failed to parse utils"); | ||
| // should not panic | ||
| DeserializeEnv::new(TypeScript::Tsx).with_utils(&utils)?; | ||
| let registration = GlobalRules::default(); | ||
| let core: crate::rule_core::SerializableRuleCore = from_str("rule: {pattern: '123'}").unwrap(); | ||
| let env_dummy = DeserializeEnv::new(TypeScript::Tsx).with_globals(®istration); | ||
| registration.insert("global-rule", core.get_matcher(env_dummy).unwrap()).unwrap(); | ||
| DeserializeEnv::new(TypeScript::Tsx).with_globals(®istration).with_utils(&utils)?; | ||
| Ok(()) | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,6 +32,10 @@ impl<R> Registration<R> { | |
| // it only insert new item to the RapidMap. It is safe to cast the raw ptr. | ||
| unsafe { &mut *(Arc::as_ptr(&self.0) as *mut RapidMap<String, R>) } | ||
| } | ||
|
|
||
| pub(crate) fn contains_key(&self, id: &str) -> bool { | ||
| self.0.contains_key(id) | ||
| } | ||
| } | ||
| pub type GlobalRules = Registration<RuleCore>; | ||
|
|
||
|
|
@@ -83,6 +87,12 @@ impl RuleRegistration { | |
| RegistrationRef { local, global } | ||
| } | ||
|
|
||
| pub(crate) fn contains_rule(&self, id: &str) -> bool { | ||
| self.local.contains_key(id) | ||
| || self.global.contains_key(id) | ||
| || self.rewriters.contains_key(id) | ||
| } | ||
|
||
|
|
||
| pub(crate) fn insert_local(&self, id: &str, rule: Rule) -> Result<(), ReferentRuleError> { | ||
| if rule.check_cyclic(id) { | ||
| return Err(ReferentRuleError::CyclicRule(id.into())); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ mod parse; | |
| mod rewrite; | ||
| mod string_case; | ||
| mod trans; | ||
| use crate::rule::referent_rule::ReferentRuleError; | ||
|
|
||
| use crate::{DeserializeEnv, RuleCore}; | ||
|
|
||
|
|
@@ -72,7 +73,7 @@ impl Transform { | |
| let map = map?; | ||
| let order = env | ||
| .get_transform_order(&map) | ||
| .map_err(TransformError::Cyclic)?; | ||
| .map_err(|e| match e { ReferentRuleError::CyclicRule(s) => TransformError::Cyclic(s), _ => TransformError::Cyclic(e.to_string()) })?; | ||
|
||
| let transforms = order | ||
| .iter() | ||
| .map(|&key| (key.to_string(), map[key].clone())) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new presence check only consults
contains_match_rule(local/global match rules). If the intent is to also error on missing rewriter references during deserialization (as described in the PR), this logic doesn’t cover rewriters, and missing rewriters can still be silently ignored elsewhere. Consider extending the validation to cover the rewriter scope as well (or clarifying the PR scope if rewriters are intentionally out of band).