diff --git a/server/error_code.md b/server/error_code.md index 69285928..28605f26 100644 --- a/server/error_code.md +++ b/server/error_code.md @@ -266,6 +266,18 @@ For compute, search, inverse arguments, this error is shown when the method is n The compute method is set to modify a certain field(s). Consider marking the modified field with the compute method +### OLS30329 +"Unknown XML ID" +The XML ID you referenced has not been found in any XML in this module or its dependencies + +### OLS30330 +"Unspecified module. Add the module name before the XML ID: 'module.xml_id'" +Your provided an XML ID that has no module specified. Specify the module which XML_ID belong to with 'module.xml_id' + +### OLS30331 +"Unknown module" +The given module is unknown + ### OLS30400 "Invalid attribute" odoo, openerp and data nodes can not contain this attribute. @@ -304,3 +316,7 @@ This attribute is not valid in a menuitem node ### OLS30445 "Data file should be an XML or a CSV file" + +### OLS30446 +"XML ID should not contain more than one dot" +An XML_ID should be in the format 'xml_id' or 'module.xml_id', but can't contains more dots diff --git a/server/src/core/evaluation.rs b/server/src/core/evaluation.rs index 9d28a7df..13d2a537 100644 --- a/server/src/core/evaluation.rs +++ b/server/src/core/evaluation.rs @@ -931,12 +931,9 @@ impl Evaluation { if base_sym.borrow().evaluations().is_some() { let parent_file_or_func = parent.clone().borrow().parent_file_or_function().as_ref().unwrap().upgrade().unwrap(); let is_in_validation = match parent_file_or_func.borrow().typ().clone() { - SymType::FILE | SymType::PACKAGE(_) => { + SymType::FILE | SymType::PACKAGE(_) | SymType::FUNCTION => { parent_file_or_func.borrow().build_status(BuildSteps::VALIDATION) == BuildStatus::IN_PROGRESS }, - SymType::FUNCTION => { - true //functions are always evaluated at validation step - } _ => {false} }; let call_parent = match base_sym_weak_eval.context.get(&S!("base_attr")){ @@ -959,6 +956,8 @@ impl Evaluation { )); } context.as_mut().unwrap().insert(S!("base_call"), ContextValue::SYMBOL(call_parent)); + context.as_mut().unwrap().insert(S!("parameters"), ContextValue::ARGUMENTS(expr.arguments.clone())); + context.as_mut().unwrap().insert(S!("is_in_validation"), ContextValue::BOOLEAN(is_in_validation)); for eval in base_sym.borrow().evaluations().unwrap().iter() { let eval_ptr = eval.symbol.get_symbol_weak_transformed(session, context, &mut diagnostics, Some(parent.borrow().get_file().unwrap().upgrade().unwrap().clone())); evals.push(Evaluation{ @@ -971,6 +970,8 @@ impl Evaluation { }); } context.as_mut().unwrap().remove(&S!("base_call")); + context.as_mut().unwrap().remove(&S!("parameters")); + context.as_mut().unwrap().remove(&S!("is_in_validation")); } } } @@ -1086,6 +1087,13 @@ impl Evaluation { if bases.len() != 1 { return AnalyzeAstResult::from_only_diagnostics(diagnostics); } + let parent_file_or_func = parent.clone().borrow().parent_file_or_function().as_ref().unwrap().upgrade().unwrap(); + let is_in_validation = match parent_file_or_func.borrow().typ().clone() { + SymType::FILE | SymType::PACKAGE(_) | SymType::FUNCTION => { + parent_file_or_func.borrow().build_status(BuildSteps::VALIDATION) == BuildStatus::IN_PROGRESS + }, + _ => {false} + }; let value = Evaluation::expr_to_str(session, &sub.slice, parent.clone(), max_infer, &mut diagnostics); diagnostics.extend(value.1); if let Some(value) = value.0 { @@ -1104,6 +1112,7 @@ impl Evaluation { context.as_mut().unwrap().insert(S!("args"), ContextValue::STRING(value)); let old_range = context.as_mut().unwrap().remove(&S!("range")); context.as_mut().unwrap().insert(S!("range"), ContextValue::RANGE(sub.slice.range())); + context.as_mut().unwrap().insert(S!("is_in_validation"), ContextValue::BOOLEAN(is_in_validation)); let hook_result = hook(session, &get_item_eval.symbol, context, &mut diagnostics, Some(parent.clone())); if let Some(hook_result) = hook_result { match hook_result { @@ -1118,6 +1127,7 @@ impl Evaluation { } } context.as_mut().unwrap().remove(&S!("args")); + context.as_mut().unwrap().remove(&S!("is_in_validation")); context.as_mut().unwrap().insert(S!("range"), old_range.unwrap()); } } diff --git a/server/src/core/python_arch_builder.rs b/server/src/core/python_arch_builder.rs index 70a7e20a..45d5079a 100644 --- a/server/src/core/python_arch_builder.rs +++ b/server/src/core/python_arch_builder.rs @@ -775,7 +775,7 @@ impl PythonArchBuilder { if add_noqa { session.noqas_stack.pop(); } - PythonArchBuilderHooks::on_class_def(session, sym); + PythonArchBuilderHooks::on_class_def(session, &self.entry_point, sym); Ok(()) } diff --git a/server/src/core/python_arch_builder_hooks.rs b/server/src/core/python_arch_builder_hooks.rs index 707cdcce..fe57024c 100644 --- a/server/src/core/python_arch_builder_hooks.rs +++ b/server/src/core/python_arch_builder_hooks.rs @@ -1,8 +1,10 @@ use std::path::PathBuf; use std::rc::Rc; use std::cell::RefCell; +use once_cell::sync::Lazy; use ruff_text_size::{TextRange, TextSize}; use tracing::warn; +use crate::core::entry_point::EntryPoint; use crate::core::symbols::symbol::Symbol; use crate::threads::SessionInfo; use crate::{Sy, S}; @@ -10,75 +12,151 @@ use crate::constants::OYarn; use super::odoo::SyncOdoo; -pub struct PythonArchBuilderHooks {} +type PythonArchClassHookFn = fn (session: &mut SessionInfo, entry: &Rc>, symbol: Rc>); -impl PythonArchBuilderHooks { +pub struct PythonArchClassHook { + pub odoo_entry: bool, + pub trees: Vec<(OYarn, OYarn, (Vec, Vec))>, + pub func: PythonArchClassHookFn +} - pub fn on_class_def(session: &mut SessionInfo, symbol: Rc>) { - let mut sym = symbol.borrow_mut(); - let name = &sym.name(); - match name.as_str() { - "BaseModel" => { - if sym.get_main_entry_tree(session) == (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel")]) { - // ----------- env ------------ - let env = sym.get_symbol(&(vec![], vec![Sy!("env")]), u32::MAX); - if env.is_empty() { - let mut range = sym.range().clone(); - let slots = sym.get_symbol(&(vec![], vec![Sy!("__slots__")]), u32::MAX); - if slots.len() == 1 { - if slots.len() == 1 { - range = slots[0].borrow().range().clone(); - } - } - sym.add_new_variable(session, Sy!("env"), &range); +#[allow(non_upper_case_globals)] +static arch_class_hooks: Lazy> = Lazy::new(|| {vec![ + PythonArchClassHook { + odoo_entry: true, + trees: vec![ + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel")])) + ], + func: |session: &mut SessionInfo, entry_point: &Rc>, symbol: Rc>| { + // ----------- env ------------ + let env = symbol.borrow().get_symbol(&(vec![], vec![Sy!("env")]), u32::MAX); + if env.is_empty() { + let mut range = symbol.borrow().range().clone(); + let slots = symbol.borrow().get_symbol(&(vec![], vec![Sy!("__slots__")]), u32::MAX); + if slots.len() == 1 { + if slots.len() == 1 { + range = slots[0].borrow().range().clone(); } } - }, - "Environment" => { - if sym.get_main_entry_tree(session) == (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("Environment")]) { - let new_sym = sym.get_symbol(&(vec![], vec![Sy!("__new__")]), u32::MAX); - let mut range = sym.range().clone(); - if new_sym.len() == 1 { - range = new_sym[0].borrow().range().clone(); - } - // ----------- env.cr ------------ - sym.add_new_variable(session, Sy!("cr"), &range); - // ----------- env.uid ------------ - let uid_sym = sym.add_new_variable(session, Sy!("uid"), &range); - uid_sym.borrow_mut().as_variable_mut().doc_string = Some(S!("The current user id (for access rights checks)")); - // ----------- env.context ------------ - let context_sym = sym.add_new_variable(session, Sy!("context"), &range); - context_sym.borrow_mut().as_variable_mut().doc_string = Some(S!("The current context")); - // ----------- env.su ------------ - let su_sym = sym.add_new_variable(session, Sy!("su"), &range); - su_sym.borrow_mut().as_variable_mut().doc_string = Some(S!("whether in superuser mode")); - // ----------- env.registry ----------- - let _ = sym.add_new_variable(session, Sy!("registry"), &range); + symbol.borrow_mut().add_new_variable(session, Sy!("env"), &range); + } + } + }, + PythonArchClassHook { + odoo_entry: true, + trees: vec![ + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("Environment")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("environments")], vec![Sy!("Environment")])) + ], + func: |session: &mut SessionInfo, entry_point: &Rc>, symbol: Rc>| { + let new_sym = symbol.borrow().get_symbol(&(vec![], vec![Sy!("__new__")]), u32::MAX); + let mut range = symbol.borrow().range().clone(); + if new_sym.len() == 1 { + range = new_sym[0].borrow().range().clone(); + } + // ----------- env.cr ------------ + symbol.borrow_mut().add_new_variable(session, Sy!("cr"), &range); + // ----------- env.uid ------------ + let uid_sym = symbol.borrow_mut().add_new_variable(session, Sy!("uid"), &range); + uid_sym.borrow_mut().as_variable_mut().doc_string = Some(S!("The current user id (for access rights checks)")); + // ----------- env.context ------------ + let context_sym = symbol.borrow_mut().add_new_variable(session, Sy!("context"), &range); + context_sym.borrow_mut().as_variable_mut().doc_string = Some(S!("The current context")); + // ----------- env.su ------------ + let su_sym = symbol.borrow_mut().add_new_variable(session, Sy!("su"), &range); + su_sym.borrow_mut().as_variable_mut().doc_string = Some(S!("whether in superuser mode")); + // ----------- env.registry ----------- + let _ = symbol.borrow_mut().add_new_variable(session, Sy!("registry"), &range); + } + }, + PythonArchClassHook { + odoo_entry: true, + trees: vec![ + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Boolean")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Integer")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Float")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Monetary")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Char")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Text")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Html")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Date")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Datetime")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Binary")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Image")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Selection")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Reference")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Many2one")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Many2oneReference")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Json")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Properties")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("PropertiesDefinition")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("One2many")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Many2many")])), + (Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Id")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_misc")], vec![Sy!("Boolean")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_numeric")], vec![Sy!("Integer")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_numeric")], vec![Sy!("Float")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_numeric")], vec![Sy!("Monetary")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_textual")], vec![Sy!("Char")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_textual")], vec![Sy!("Text")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_textual")], vec![Sy!("Html")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_temporal")], vec![Sy!("Date")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_temporal")], vec![Sy!("Datetime")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_binary")], vec![Sy!("Binary")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_binary")], vec![Sy!("Image")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_selection")], vec![Sy!("Selection")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_reference")], vec![Sy!("Reference")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_relational")], vec![Sy!("Many2one")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_reference")], vec![Sy!("Many2oneReference")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_misc")], vec![Sy!("Json")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_properties")], vec![Sy!("Properties")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_properties")], vec![Sy!("PropertiesDefinition")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_relational")], vec![Sy!("One2many")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_relational")], vec![Sy!("Many2many")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_misc")], vec![Sy!("Id")])), + ], + func: |session: &mut SessionInfo, entry_point: &Rc>, symbol: Rc>| { + // ----------- __get__ ------------ + let get_sym = symbol.borrow().get_symbol(&(vec![], vec![Sy!("__get__")]), u32::MAX); + if get_sym.is_empty() { + let range = symbol.borrow().range().clone(); + symbol.borrow_mut().add_new_function(session, &S!("__get__"), &range, &range.end()); + } else { + if !["Id", "One2many"].contains(&symbol.borrow().name().as_str()){ + warn!("Found __get__ function for field of name ({})", symbol.borrow().name()); } - }, - "Boolean" | "Integer" | "Float" | "Monetary" | "Char" | "Text" | "Html" | "Date" | "Datetime" | - "Binary" | "Image" | "Selection" | "Reference" | "Many2one" | "Many2oneReference" | "Json" | - "Properties" | "PropertiesDefinition" | "One2many" | "Many2many" | "Id" => { - if sym.get_main_entry_tree(session).0 == vec![Sy!("odoo"), Sy!("fields")] { - // ----------- __get__ ------------ - let get_sym = sym.get_symbol(&(vec![], vec![Sy!("__get__")]), u32::MAX); - if get_sym.is_empty() { - let range = sym.range().clone(); - sym.add_new_function(session, &S!("__get__"), &range, &range.end()); - } else { - if !["Id", "One2many"].contains(&name.as_str()){ - warn!("Found __get__ function for field of name ({})", name); - } - } - // ----------- __init__ ------------ - let get_sym = sym.get_symbol(&(vec![], vec![Sy!("__init__")]), u32::MAX); - if get_sym.is_empty() { - let range = sym.range().clone(); - sym.add_new_function(session, &S!("__init__"), &range, &range.end()); + } + // ----------- __init__ ------------ + let get_sym = symbol.borrow().get_symbol(&(vec![], vec![Sy!("__init__")]), u32::MAX); + if get_sym.is_empty() { + let range = symbol.borrow().range().clone(); + symbol.borrow_mut().add_new_function(session, &S!("__init__"), &range, &range.end()); + } + } + }, +]}); + +pub struct PythonArchBuilderHooks {} + +impl PythonArchBuilderHooks { + + pub fn on_class_def(session: &mut SessionInfo, entry_point: &Rc>, symbol: Rc>) { + let tree = symbol.borrow().get_tree(); + let odoo_tree = symbol.borrow().get_main_entry_tree(session); + let name = symbol.borrow().name().clone(); + for hook in arch_class_hooks.iter() { + for hook_tree in hook.trees.iter() { + if hook_tree.0 >= session.sync_odoo.full_version || + hook_tree.1 <= session.sync_odoo.full_version { + continue; //skip if version not in range + } + if name.eq(hook_tree.2.1.last().unwrap()) { + if (hook.odoo_entry && session.sync_odoo.has_main_entry && odoo_tree == hook_tree.2) || (!hook.odoo_entry && tree == hook_tree.2) { + (hook.func)(session, entry_point, symbol.clone()); } } } - _ => {} } } diff --git a/server/src/core/python_arch_eval_hooks.rs b/server/src/core/python_arch_eval_hooks.rs index fa397a7f..28dea0d0 100644 --- a/server/src/core/python_arch_eval_hooks.rs +++ b/server/src/core/python_arch_eval_hooks.rs @@ -32,20 +32,17 @@ type PythonArchEvalHookFile = fn (odoo: &mut SyncOdoo, entry: &Rc, - pub content_tree: Vec, //if set, will provide symbol in file content instead of the file symbol to func + pub trees: Vec<(OYarn, OYarn, (Vec, Vec))>, //if tree content is set, will provide symbol in file content instead of the file symbol to func pub if_exist_only: bool, - pub min_version: OYarn, - pub max_version: OYarn, pub func: PythonArchEvalHookFile } #[allow(non_upper_case_globals)] static arch_eval_file_hooks: Lazy> = Lazy::new(|| {vec![ PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("models")], - content_tree: vec![Sy!("BaseModel"), Sy!("env")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("env")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("env")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, file_symbol: Rc>, symbol: Rc>| { let env_file = odoo.get_symbol(odoo.config.odoo_path.as_ref().unwrap(), &(vec![Sy!("odoo"), Sy!("api")], vec![]), u32::MAX); let env_class = odoo.get_symbol(odoo.config.odoo_path.as_ref().unwrap(), &(vec![Sy!("odoo"), Sy!("api")], vec![Sy!("Environment")]), u32::MAX); @@ -68,9 +65,9 @@ static arch_eval_file_hooks: Lazy> = Lazy::new(|| {v } }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("models")], - content_tree: vec![Sy!("BaseModel"), Sy!("ids")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("ids")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("ids")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { let values: Vec = Vec::new(); let mut id = symbol.borrow_mut(); @@ -87,9 +84,9 @@ static arch_eval_file_hooks: Lazy> = Lazy::new(|| {v id.set_evaluations(vec![Evaluation::eval_from_symbol(odoo, values, range.clone())]); }},*/ PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("api")], - content_tree: vec![Sy!("Environment"), Sy!("registry")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("Environment"), Sy!("registry")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("environments")], vec![Sy!("Environment"), Sy!("registry")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { let registry_sym = odoo.get_symbol(odoo.config.odoo_path.as_ref().unwrap(), &(vec![Sy!("odoo"), Sy!("modules"), Sy!("registry")], vec![Sy!("Registry")]), u32::MAX); if !registry_sym.is_empty() { @@ -107,160 +104,159 @@ static arch_eval_file_hooks: Lazy> = Lazy::new(|| {v }}, /* As __get__ doesn't exists in each class, the validator will not trigger hooks for them at function level, so we put it at file level. */ PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Boolean")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Boolean")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_misc")], vec![Sy!("Boolean")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("bool")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Integer")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Integer")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_numeric")], vec![Sy!("Integer")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("int")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Float")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Float")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_numeric")], vec![Sy!("Float")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("float")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Monetary")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Monetary")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_numeric")], vec![Sy!("Monetary")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("float")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Char")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Char")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_textual")], vec![Sy!("Char")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("str")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Text")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Text")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_textual")], vec![Sy!("Text")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("str")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Html")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Html")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_textual")], vec![Sy!("Html")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("markupsafe")], vec![Sy!("Markup")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Date")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Date")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_temporal")], vec![Sy!("Date")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("datetime")], vec![Sy!("date")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Datetime")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Datetime")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_temporal")], vec![Sy!("Datetime")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("datetime")], vec![Sy!("datetime")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Binary")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Binary")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_binary")], vec![Sy!("Binary")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("bytes")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Image")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Image")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_binary")], vec![Sy!("Image")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("bytes")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Selection")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Selection")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_selection")], vec![Sy!("Selection")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("str")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Reference")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Reference")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_reference")], vec![Sy!("Reference")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("str")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Json")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Json")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_misc")], vec![Sy!("Json")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("object")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Properties")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Properties")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_properties")], vec![Sy!("Properties")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("object")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("PropertiesDefinition")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("PropertiesDefinition")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_properties")], vec![Sy!("PropertiesDefinition")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval(odoo, entry, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("object")])); PythonArchEvalHooks::_update_field_init(symbol.clone(), false); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Many2one")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Many2one")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_relational")], vec![Sy!("Many2one")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval_relational(symbol.clone()); PythonArchEvalHooks::_update_field_init(symbol.clone(), true); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("One2many")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("One2many")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_relational")], vec![Sy!("One2many")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_field_init(symbol.clone(), true); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("fields")], - content_tree: vec![Sy!("Many2many")], - if_exist_only: true, min_version: Sy!("0.0"), max_version: Sy!("999.0"), + trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Many2many")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_relational")], vec![Sy!("Many2many")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval_relational(symbol.clone()); PythonArchEvalHooks::_update_field_init(symbol.clone(), true); }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("init")], - content_tree: vec![Sy!("_")], - if_exist_only: true, min_version: Sy!("18.1"), max_version: Sy!("999.0"), + trees: vec![(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("init")], vec![Sy!("_")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { let odoo_underscore = odoo.get_symbol(_file_symbol.borrow().paths()[0].as_str(), &(vec![Sy!("odoo")], vec![Sy!("_")]), u32::MAX); if let Some(eval_1) = odoo_underscore.first() { @@ -268,9 +264,8 @@ static arch_eval_file_hooks: Lazy> = Lazy::new(|| {v } }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("init")], - content_tree: vec![Sy!("SUPERUSER_ID")], - if_exist_only: true, min_version: Sy!("18.1"), max_version: Sy!("999.0"), + trees: vec![(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("init")], vec![Sy!("SUPERUSER_ID")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { let odoo_superuser_id = odoo.get_symbol(_file_symbol.borrow().paths()[0].as_str(), &(vec![Sy!("odoo")], vec![Sy!("SUPERUSER_ID")]), u32::MAX); if let Some(eval_1) = odoo_superuser_id.first() { @@ -278,9 +273,8 @@ static arch_eval_file_hooks: Lazy> = Lazy::new(|| {v } }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("init")], - content_tree: vec![Sy!("_lt")], - if_exist_only: true, min_version: Sy!("18.1"), max_version: Sy!("999.0"), + trees: vec![(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("init")], vec![Sy!("_lt")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { let odoo_lt = odoo.get_symbol(_file_symbol.borrow().paths()[0].as_str(), &(vec![Sy!("odoo")], vec![Sy!("_lt")]), u32::MAX); if let Some(eval_1) = odoo_lt.first() { @@ -288,9 +282,8 @@ static arch_eval_file_hooks: Lazy> = Lazy::new(|| {v } }}, PythonArchEvalFileHook {odoo_entry: true, - file_tree: vec![Sy!("odoo"), Sy!("init")], - content_tree: vec![Sy!("Command")], - if_exist_only: true, min_version: Sy!("18.1"), max_version: Sy!("999.0"), + trees: vec![(Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("init")], vec![Sy!("Command")]))], + if_exist_only: true, func: |odoo: &mut SyncOdoo, entry: &Rc>, _file_symbol: Rc>, symbol: Rc>| { let odoo_command = odoo.get_symbol(_file_symbol.borrow().paths()[0].as_str(), &(vec![Sy!("odoo")], vec![Sy!("Command")]), u32::MAX); if let Some(eval_1) = odoo_command.first() { @@ -303,7 +296,7 @@ type PythonArchEvalHookFunc = fn (odoo: &mut SyncOdoo, entry_point: &Rc, //min_version, max_version, tree pub if_exist_only: bool, pub func: PythonArchEvalHookFunc } @@ -311,7 +304,8 @@ pub struct PythonArchEvalFunctionHook { #[allow(non_upper_case_globals)] static arch_eval_function_hooks: Lazy> = Lazy::new(|| {vec![ PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("Environment"), Sy!("__getitem__")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("Environment"), Sy!("__getitem__")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("environments")], vec![Sy!("Environment"), Sy!("__getitem__")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { symbol.borrow_mut().set_evaluations(vec![Evaluation { @@ -325,7 +319,8 @@ static arch_eval_function_hooks: Lazy> = Lazy::n }]); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("modules"), Sy!("registry")], vec![Sy!("Registry"), Sy!("__getitem__")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("modules"), Sy!("registry")], vec![Sy!("Registry"), Sy!("__getitem__")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("registry")], vec![Sy!("Registry"), Sy!("__getitem__")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { symbol.borrow_mut().set_evaluations(vec![Evaluation { @@ -339,31 +334,36 @@ static arch_eval_function_hooks: Lazy> = Lazy::n }]); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("__iter__")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("__iter__")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("Registry"), Sy!("__getitem__")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { symbol.borrow_mut().set_evaluations(vec![Evaluation::new_self()]); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_env")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_env")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_env")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { symbol.borrow_mut().set_evaluations(vec![Evaluation::new_self()]); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("sudo")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("sudo")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("sudo")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { symbol.borrow_mut().set_evaluations(vec![Evaluation::new_self()]); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("create")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("create")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("create")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { symbol.borrow_mut().set_evaluations(vec![Evaluation::new_self()]); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("search")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("search")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("search")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { let mut search: std::cell::RefMut = symbol.borrow_mut(); @@ -379,84 +379,97 @@ static arch_eval_function_hooks: Lazy> = Lazy::n } }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("browse")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("browse")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("browse")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { symbol.borrow_mut().set_evaluations(vec![Evaluation::new_self()]); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_company")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_company")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_company")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { symbol.borrow_mut().set_evaluations(vec![Evaluation::new_self()]); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_context")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_context")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_context")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { symbol.borrow_mut().set_evaluations(vec![Evaluation::new_self()]); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_prefetch")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_prefetch")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_prefetch")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { symbol.borrow_mut().set_evaluations(vec![Evaluation::new_self()]); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_user")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_user")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_user")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { symbol.borrow_mut().set_evaluations(vec![Evaluation::new_self()]); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("with_env")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("exists")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("exists")]))], if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { symbol.borrow_mut().set_evaluations(vec![Evaluation::new_self()]); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel"), Sy!("exists")]), + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Id"), Sy!("__get__")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_misc")], vec![Sy!("Id"), Sy!("__get__")]))], //We have to put it at function level hook to remove evaluation from existing code if_exist_only: true, func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { - symbol.borrow_mut().set_evaluations(vec![Evaluation::new_self()]); - }}, - PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("Id"), Sy!("__get__")]), //We have to put it at function level hook to remove evaluation from existing code - if_exist_only: true, - func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval_func_level(odoo, &entry_point, symbol.clone(), (vec![Sy!("builtins")], vec![Sy!("int")])); }}, PythonArchEvalFunctionHook {odoo_entry: true, - tree: (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("One2many"), Sy!("__get__")]), - if_exist_only: true, - func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("fields")], vec![Sy!("One2many"), Sy!("__get__")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("fields_relational")], vec![Sy!("One2many"), Sy!("__get__")]))], + if_exist_only: true, + func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { PythonArchEvalHooks::_update_get_eval_func_relational(symbol.clone()); }}, + PythonArchEvalFunctionHook { + odoo_entry: true, + tree: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("Environment"), Sy!("ref")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("environments")], vec![Sy!("Environment"), Sy!("ref")]))], + if_exist_only: true, + func: |odoo: &mut SyncOdoo, entry_point: &Rc>, symbol: Rc>| { + PythonArchEvalHooks::_validation_env_ref(symbol.clone()); + }}, ]}); type PythonArchEvalHookDecorator = fn (session: &mut SessionInfo, func_sym: Rc>, arguments: &Arguments) -> Vec; pub struct PythonArchEvalDecoratorHook { - pub tree: Tree, + pub trees: Vec<(OYarn, OYarn, Tree)>, //min_version, max_version, tree pub func: PythonArchEvalHookDecorator } #[allow(non_upper_case_globals)] static arch_eval_decorator_hooks: Lazy> = Lazy::new(|| {vec![ - PythonArchEvalDecoratorHook {tree: (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("returns")]), + PythonArchEvalDecoratorHook {trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("returns")]))], //disappear in 18.1 func: |session: &mut SessionInfo, func_sym: Rc>, arguments: &Arguments| { PythonArchEvalHooks::handle_api_returns_decorator(session, func_sym, arguments) }}, - PythonArchEvalDecoratorHook {tree: (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("onchange")]), + PythonArchEvalDecoratorHook {trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("onchange")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("decorators")], vec![Sy!("onchange")]))], func: |session: &mut SessionInfo, func_sym: Rc>, arguments: &Arguments| { PythonArchEvalHooks::handle_api_simple_field_decorator(session, func_sym, arguments) }}, - PythonArchEvalDecoratorHook {tree: (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("constrains")]), + PythonArchEvalDecoratorHook {trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("constrains")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("decorators")], vec![Sy!("constrains")]))], func: |session: &mut SessionInfo, func_sym: Rc>, arguments: &Arguments| { PythonArchEvalHooks::handle_api_simple_field_decorator(session, func_sym, arguments) }}, - PythonArchEvalDecoratorHook {tree: (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("depends")]), + PythonArchEvalDecoratorHook {trees: vec![(Sy!("0.0"), Sy!("18.0"), (vec![Sy!("odoo"), Sy!("api")], vec![Sy!("depends")])), + (Sy!("18.1"), Sy!("999.0"), (vec![Sy!("odoo"), Sy!("orm"), Sy!("decorators")], vec![Sy!("depends")]))], func: |session: &mut SessionInfo, func_sym: Rc>, arguments: &Arguments| { PythonArchEvalHooks::handle_api_nested_field_decorator(session, func_sym, arguments) }}, @@ -471,14 +484,20 @@ impl PythonArchEvalHooks { let odoo_tree = symbol.borrow().get_main_entry_tree(session); let name = symbol.borrow().name().clone(); for hook in arch_eval_file_hooks.iter() { - if name.eq(hook.file_tree.last().unwrap()) && - ((hook.odoo_entry && session.sync_odoo.has_main_entry && odoo_tree.0 == hook.file_tree) || (!hook.odoo_entry && tree.0 == hook.file_tree)) { - if hook.content_tree.is_empty() { - (hook.func)(session.sync_odoo, entry_point, symbol.clone(), symbol.clone()); - } else { - let sub_symbol = symbol.borrow().get_symbol(&(vec![], hook.content_tree.clone()), u32::MAX); - if !sub_symbol.is_empty() { - (hook.func)(session.sync_odoo, entry_point, symbol.clone(), sub_symbol.last().unwrap().clone()); + for (min_version, max_version, hook_tree) in hook.trees.iter() { + if min_version >= &session.sync_odoo.full_version || + max_version <= &session.sync_odoo.full_version { + continue; //skip if version not in range + } + if name.eq(hook_tree.0.last().unwrap()) && + ((hook.odoo_entry && session.sync_odoo.has_main_entry && odoo_tree.0 == hook_tree.1) || (!hook.odoo_entry && tree.0 == hook_tree.1)) { + if hook_tree.0.is_empty() { + (hook.func)(session.sync_odoo, entry_point, symbol.clone(), symbol.clone()); + } else { + let sub_symbol = symbol.borrow().get_symbol(&(vec![], hook_tree.0.clone()), u32::MAX); + if !sub_symbol.is_empty() { + (hook.func)(session.sync_odoo, entry_point, symbol.clone(), sub_symbol.last().unwrap().clone()); + } } } } @@ -490,9 +509,15 @@ impl PythonArchEvalHooks { let odoo_tree = symbol.borrow().get_main_entry_tree(session); let name = symbol.borrow().name().clone(); for hook in arch_eval_function_hooks.iter() { - if name.eq(hook.tree.1.last().unwrap()) { - if (hook.odoo_entry && session.sync_odoo.has_main_entry && odoo_tree == hook.tree) || (!hook.odoo_entry && tree == hook.tree) { - (hook.func)(session.sync_odoo, entry_point, symbol.clone()); + for hook_tree in hook.tree.iter() { + if hook_tree.0 >= session.sync_odoo.full_version || + hook_tree.1 <= session.sync_odoo.full_version { + continue; //skip if version not in range + } + if name.eq(hook_tree.2.1.last().unwrap()) { + if (hook.odoo_entry && session.sync_odoo.has_main_entry && odoo_tree == hook_tree.2) || (!hook.odoo_entry && tree == hook_tree.2) { + (hook.func)(session.sync_odoo, entry_point, symbol.clone()); + } } } } @@ -531,10 +556,16 @@ impl PythonArchEvalHooks { let Some(dec_sym) = decorator_eval_sym_weak.weak.upgrade() else {continue}; let dec_sym_tree = dec_sym.borrow().get_tree(); for hook in arch_eval_decorator_hooks.iter() { - if !dec_sym_tree.0.ends_with(&hook.tree.0) || !dec_sym_tree.1.ends_with(&hook.tree.1) || !SyncOdoo::is_in_main_entry(session, &dec_sym_tree.0) { - continue; + for (min_version, max_version, hook_tree) in hook.trees.iter() { + if min_version > &session.sync_odoo.full_version || + max_version < &session.sync_odoo.full_version { + continue; //skip if version not in range + } + if !dec_sym_tree.0.ends_with(&hook_tree.0) || !dec_sym_tree.1.ends_with(&hook_tree.1) || !SyncOdoo::is_in_main_entry(session, &dec_sym_tree.0) { + continue; + } + diagnostics.extend((hook.func)(session, func_sym.clone(), decorator_args)); } - diagnostics.extend((hook.func)(session, func_sym.clone(), decorator_args)); } } } @@ -544,6 +575,7 @@ impl PythonArchEvalHooks { pub fn eval_env_get_item(session: &mut SessionInfo, evaluation_sym: &EvaluationSymbol, context: &mut Option, diagnostics: &mut Vec, scope: Option>>) -> Option { if let Some(context) = context { + let in_validation = context.get(&S!("is_in_validation")).unwrap_or(&ContextValue::BOOLEAN(false)).as_bool(); let arg = context.get(&S!("args")); if let Some(arg) = arg { match arg { @@ -579,54 +611,62 @@ impl PythonArchEvalHooks { //retry without from_module to see if model exists elsewhere let symbols = model.get_main_symbols(session, None); if symbols.is_empty() { - let range = FileMgr::textRange_to_temporary_Range(&context.get(&S!("range")).unwrap().as_text_range()); - add_diagnostic(diagnostics, Diagnostic::new(range, - Some(DiagnosticSeverity::ERROR), - Some(NumberOrString::String(S!("OLS30105"))), - Some(EXTENSION_NAME.to_string()), - S!("This model is inherited, but never declared."), - None, - None - ) - , &session.current_noqa); + if in_validation { + let range = FileMgr::textRange_to_temporary_Range(&context.get(&S!("range")).unwrap().as_text_range()); + add_diagnostic(diagnostics, Diagnostic::new(range, + Some(DiagnosticSeverity::ERROR), + Some(NumberOrString::String(S!("OLS30105"))), + Some(EXTENSION_NAME.to_string()), + S!("This model is inherited, but never declared."), + None, + None + ) + , &session.current_noqa); + } } else { + if in_validation { + let range = FileMgr::textRange_to_temporary_Range(&context.get(&S!("range")).unwrap().as_text_range()); + let valid_modules: Vec = symbols.iter().map(|s| match s.borrow().find_module() { + Some(sym) => sym.borrow().name().clone(), + None => Sy!("Unknown").clone() + }).collect(); + add_diagnostic(diagnostics, Diagnostic::new(range, + Some(DiagnosticSeverity::ERROR), + Some(NumberOrString::String(S!("OLS30101"))), + Some(EXTENSION_NAME.to_string()), + format!("This model is not declared in the dependencies of your module. You should consider adding one of the following dependency: {:?}", valid_modules), + None, + None + ) + , &session.current_noqa); + } + } + } else { + if in_validation { let range = FileMgr::textRange_to_temporary_Range(&context.get(&S!("range")).unwrap().as_text_range()); - let valid_modules: Vec = symbols.iter().map(|s| match s.borrow().find_module() { - Some(sym) => sym.borrow().name().clone(), - None => Sy!("Unknown").clone() - }).collect(); add_diagnostic(diagnostics, Diagnostic::new(range, Some(DiagnosticSeverity::ERROR), - Some(NumberOrString::String(S!("OLS30101"))), + Some(NumberOrString::String(S!("OLS30102"))), Some(EXTENSION_NAME.to_string()), - format!("This model is not declared in the dependencies of your module. You should consider adding one of the following dependency: {:?}", valid_modules), + S!("Unknown model. Check your addons path"), None, None - ) - , &session.current_noqa); + ), &session.current_noqa); } - } else { - let range = FileMgr::textRange_to_temporary_Range(&context.get(&S!("range")).unwrap().as_text_range()); - add_diagnostic(diagnostics, Diagnostic::new(range, - Some(DiagnosticSeverity::ERROR), - Some(NumberOrString::String(S!("OLS30102"))), - Some(EXTENSION_NAME.to_string()), - S!("Unknown model. Check your addons path"), - None, - None - ), &session.current_noqa); } } } else { let range = FileMgr::textRange_to_temporary_Range(&context.get(&S!("range")).unwrap().as_text_range()); - add_diagnostic(diagnostics, Diagnostic::new(range, - Some(DiagnosticSeverity::ERROR), - Some(NumberOrString::String(S!("OLS30102"))), - None, - S!("Unknown model. Check your addons path"), - None, - None - ), &session.current_noqa); + if in_validation { + add_diagnostic(diagnostics, Diagnostic::new(range, + Some(DiagnosticSeverity::ERROR), + Some(NumberOrString::String(S!("OLS30102"))), + None, + S!("Unknown model. Check your addons path"), + None, + None + ), &session.current_noqa); + } } } _ => { @@ -1007,4 +1047,92 @@ impl PythonArchEvalHooks { diagnostics } + fn eval_env_ref(session: &mut SessionInfo, evaluation_sym: &EvaluationSymbol, context: &mut Option, diagnostics: &mut Vec, scope: Option>>) -> Option { + let Some(context) = context else {return None}; + let in_validation = context.get(&S!("is_in_validation")).unwrap_or(&ContextValue::BOOLEAN(false)).as_bool(); + let Some(parameters) = context.get(&S!("parameters")).map(|ps| ps.as_arguments()) else {return None}; + if parameters.args.is_empty() { + return None; // No arguments to process + } + if !parameters.args[0].is_string_literal_expr() { + return None; + } + if parameters.keywords.len() == 1 { + if parameters.keywords[0].value.as_boolean_literal_expr().unwrap().value == false { + return None; // No need to process if the second argument (raise_if_not_found) is false + } + } + let xml_id_expr = parameters.args[0].as_string_literal_expr().unwrap(); + let xml_id_str = xml_id_expr.value.to_str(); + let mut xml_id_split = xml_id_str.split('.'); + let module_name = xml_id_split.next().unwrap(); + let mut xml_id = xml_id_split.collect::>().join("."); + let mut module = session.sync_odoo.modules.get(module_name).cloned(); + if module.is_none() { + if in_validation { + if xml_id.len() == 0 { + diagnostics.push(Diagnostic::new( + FileMgr::textRange_to_temporary_Range(&xml_id_expr.range()), + Some(DiagnosticSeverity::ERROR), + Some(NumberOrString::String(S!("OLS30330"))), + Some(EXTENSION_NAME.to_string()), + S!("Unspecified module. Add the module name before the XML ID: 'module.xml_id'"), + None, + None + )); + } else { + diagnostics.push(Diagnostic::new( + FileMgr::textRange_to_temporary_Range(&xml_id_expr.range()), + Some(DiagnosticSeverity::ERROR), + Some(NumberOrString::String(S!("OLS30331"))), + Some(EXTENSION_NAME.to_string()), + S!("Unknown module"), + None, + None + )); + } + } + return None; + } + let Some(module_rc) = module.unwrap().upgrade() else { + return None; + }; + let module_rc_bw = module_rc.borrow(); + let Some(symbol) = module_rc_bw.as_module_package().xml_ids.get(xml_id.as_str()) else { + if in_validation { + diagnostics.push(Diagnostic::new( + FileMgr::textRange_to_temporary_Range(&xml_id_expr.range()), + Some(DiagnosticSeverity::ERROR), + Some(NumberOrString::String(S!("OLS30329"))), + Some(EXTENSION_NAME.to_string()), + S!("Unknown XML ID"), + None, + None + )); + } + return None; + }; + //TODO => csv xml_id + //TODO check module dependencies + //TODO in xml ONLY, ref can omit the 'module.' before the xml_id + //TODO implement base.model_'nameofmodel' - to test + return None; //TODO implement returned value + } + + fn _validation_env_ref(func_sym: Rc>) -> Vec { + let mut diagnostics = vec![]; + func_sym.borrow_mut().set_evaluations(vec![Evaluation { + symbol: EvaluationSymbol::new_with_symbol( + Rc::downgrade(&func_sym), + Some(true), + HashMap::new(), + Some(PythonArchEvalHooks::eval_env_ref) + ), + value: None, + range: None + }]); + + diagnostics + } + } diff --git a/server/src/core/python_odoo_builder.rs b/server/src/core/python_odoo_builder.rs index 9c39ba2d..74b5e78f 100644 --- a/server/src/core/python_odoo_builder.rs +++ b/server/src/core/python_odoo_builder.rs @@ -49,6 +49,10 @@ impl PythonOdooBuilder { Some(model) => model.borrow_mut().add_symbol(session, sym.clone()), None => { let model = Model::new(model_name.clone(), sym.clone()); + session.sync_odoo.modules.get("base").map(|module| { + let xml_id_model_name = oyarn!("model_{}", model_name.replace(".", "_").as_str()); + module.upgrade().unwrap().borrow_mut().as_module_package_mut().xml_ids.insert(xml_id_model_name, vec![Rc::downgrade(&sym)]); + }); session.sync_odoo.models.insert(model_name.clone(), Rc::new(RefCell::new(model))); } } @@ -294,17 +298,29 @@ impl PythonOdooBuilder { let symbol = &self.symbol.clone(); let odoo_symbol_tree = symbol.borrow().get_main_entry_tree(session); let mut sym = symbol.borrow_mut(); - if odoo_symbol_tree.0.len() == 2 && odoo_symbol_tree.1.len() == 1 && odoo_symbol_tree.0[0] == "odoo" && odoo_symbol_tree.0[1] == "models" && + if session.sync_odoo.full_version < S!("18.1") && odoo_symbol_tree.0.len() == 2 && odoo_symbol_tree.1.len() == 1 && odoo_symbol_tree.0[0] == "odoo" && odoo_symbol_tree.0[1] == "models" && (odoo_symbol_tree.1[0] == "BaseModel" || odoo_symbol_tree.1[0] == "Model" || odoo_symbol_tree.1[0] == "TransientModel") { - //we don't want to compare these classes with themselves + //we don't want to compare these classes with themselves (<18.1) return false; - } else { + } else if session.sync_odoo.full_version >= S!("18.1") && odoo_symbol_tree.0.len() == 3 && odoo_symbol_tree.1.len() == 1 && odoo_symbol_tree.0[0] == "odoo" && odoo_symbol_tree.0[1] == "orm" && odoo_symbol_tree.0[2] == "models" && + (odoo_symbol_tree.1[0] == "BaseModel" || odoo_symbol_tree.1[0] == "Model" || odoo_symbol_tree.1[0] == "TransientModel") { + //we don't want to compare these classes with themselves (> 18.1) + return false; + }else { if sym.as_class_sym().bases.is_empty() { return false; } - let base_model = session.sync_odoo.get_symbol(session.sync_odoo.config.odoo_path.as_ref().unwrap(), &(vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel")]), u32::MAX); - let model = session.sync_odoo.get_symbol(session.sync_odoo.config.odoo_path.as_ref().unwrap(), &(vec![Sy!("odoo"), Sy!("models")], vec![Sy!("Model")]), u32::MAX); - let transient = session.sync_odoo.get_symbol(session.sync_odoo.config.odoo_path.as_ref().unwrap(), &(vec![Sy!("odoo"), Sy!("models")], vec![Sy!("TransientModel")]), u32::MAX); + let mut base_model_tree = (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("BaseModel")]); + let mut model_tree = (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("Model")]); + let mut transient_tree = (vec![Sy!("odoo"), Sy!("models")], vec![Sy!("TransientModel")]); + if session.sync_odoo.full_version >= S!("18.1") { + base_model_tree = (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("BaseModel")]); + model_tree = (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("Model")]); + transient_tree = (vec![Sy!("odoo"), Sy!("orm"), Sy!("models")], vec![Sy!("TransientModel")]); + } + let base_model = session.sync_odoo.get_symbol(session.sync_odoo.config.odoo_path.as_ref().unwrap(), &base_model_tree, u32::MAX); + let model = session.sync_odoo.get_symbol(session.sync_odoo.config.odoo_path.as_ref().unwrap(), &model_tree, u32::MAX); + let transient = session.sync_odoo.get_symbol(session.sync_odoo.config.odoo_path.as_ref().unwrap(), &transient_tree, u32::MAX); if base_model.is_empty() || model.is_empty() || transient.is_empty() { //one of them is not already loaded, but that's not really an issue, as now odoo step has been merged //with arch eval step, some files will be odooed before loading the orm fully. In this case we should diff --git a/server/src/core/symbols/module_symbol.rs b/server/src/core/symbols/module_symbol.rs index 675cf089..3b241236 100644 --- a/server/src/core/symbols/module_symbol.rs +++ b/server/src/core/symbols/module_symbol.rs @@ -41,6 +41,7 @@ pub struct ModuleSymbol { all_depends: HashSet, //computed all depends to avoid too many recomputations data: Vec<(String, TextRange)>, // TODO pub module_symbols: HashMap>>, + pub xml_ids: HashMap>>>, pub arch_status: BuildStatus, pub arch_eval_status: BuildStatus, pub odoo_status: BuildStatus, @@ -79,6 +80,7 @@ impl ModuleSymbol { root_path: dir_path.sanitize(), loaded: false, module_name: OYarn::from(""), + xml_ids: HashMap::new(), dir_name: OYarn::from(""), depends: vec!((OYarn::from("base"), TextRange::default())), all_depends: HashSet::new(), @@ -358,8 +360,8 @@ impl ModuleSymbol { if let Ok(document) = document { file_info.replace_diagnostics(BuildSteps::SYNTAX, vec![]); let root = document.root_element(); - let mut xml_builder = XmlArchBuilder::new(); - xml_builder.load_arch(session, xml_sym, &mut file_info, &root); + let mut xml_builder = XmlArchBuilder::new(xml_sym); + xml_builder.load_arch(session, &mut file_info, &root); file_info.publish_diagnostics(session); //TODO do it only if diagnostics are not empty, else in validation } else if data.len() > 0 { let mut diagnostics = vec![]; diff --git a/server/src/core/symbols/symbol.rs b/server/src/core/symbols/symbol.rs index 10153772..c1433bf2 100644 --- a/server/src/core/symbols/symbol.rs +++ b/server/src/core/symbols/symbol.rs @@ -2477,10 +2477,36 @@ impl Symbol { pub fn is_field_class(&self, session: &mut SessionInfo) -> bool { let tree = flatten_tree(&self.get_main_entry_tree(session)); - if tree.len() == 3 && tree[0] == "odoo" && tree[1] == "fields" { - if matches!(tree[2].as_str(), "Boolean" | "Integer" | "Float" | "Monetary" | "Char" | "Text" | "Html" | "Date" | "Datetime" | - "Binary" | "Image" | "Selection" | "Reference" | "Json" | "Properties" | "PropertiesDefinition" | "Id" | "Many2one" | "One2many" | "Many2many" | "Many2oneReference") { - return true; + if session.sync_odoo.full_version <= S!("18.0") { + if tree.len() == 3 && tree[0] == "odoo" && tree[1] == "fields" { + if matches!(tree[2].as_str(), "Boolean" | "Integer" | "Float" | "Monetary" | "Char" | "Text" | "Html" | "Date" | "Datetime" | + "Binary" | "Image" | "Selection" | "Reference" | "Json" | "Properties" | "PropertiesDefinition" | "Id" | "Many2one" | "One2many" | "Many2many" | "Many2oneReference") { + return true; + } + } + } else { + if tree.len() == 4 && tree[0] == "odoo" && tree[1] == "orm" { + return tree[2] == "fields_misc" && tree[3] == "Boolean" || + tree[2] == "fields_numeric" && tree[3] == "Integer" || + tree[2] == "fields_numeric" && tree[3] == "Float" || + tree[2] == "fields_numeric" && tree[3] == "Monetary" || + tree[2] == "fields_textual" && tree[3] == "Char" || + tree[2] == "fields_textual" && tree[3] == "Text" || + tree[2] == "fields_textual" && tree[3] == "Html" || + tree[2] == "fields_temporal" && tree[3] == "Date" || + tree[2] == "fields_temporal" && tree[3] == "Datetime" || + tree[2] == "fields_binary" && tree[3] == "Binary" || + tree[2] == "fields_binary" && tree[3] == "Image" || + tree[2] == "fields_selection" && tree[3] == "Selection" || + tree[2] == "fields_reference" && tree[3] == "Reference" || + tree[2] == "fields_relational" && tree[3] == "Many2one" || + tree[2] == "fields_reference" && tree[3] == "Many2oneReference" || + tree[2] == "fields_misc" && tree[3] == "Json" || + tree[2] == "fields_properties" && tree[3] == "Properties" || + tree[2] == "fields_properties" && tree[3] == "PropertiesDefinition" || + tree[2] == "fields_relational" && tree[3] == "One2many" || + tree[2] == "fields_relational" && tree[3] == "Many2many" || + tree[2] == "fields_misc" && tree[3] == "Id"; } } false @@ -2488,12 +2514,9 @@ impl Symbol { pub fn is_specific_field_class(&self, session: &mut SessionInfo, field_names: &[&str]) -> bool { let tree = flatten_tree(&self.get_main_entry_tree(session)); - if tree.len() == 3 && tree[0] == "odoo" && tree[1] == "fields" { - if field_names.contains(&tree[2].as_str()) { - return true; - } - } - false + return self.is_field(session) && field_names.iter().any(|&name| { + tree.last().unwrap() == name + }) } pub fn is_specific_field(&self, session: &mut SessionInfo, field_names: &[&str]) -> bool { diff --git a/server/src/core/xml_arch_builder.rs b/server/src/core/xml_arch_builder.rs index 4df205b9..9910244d 100644 --- a/server/src/core/xml_arch_builder.rs +++ b/server/src/core/xml_arch_builder.rs @@ -5,7 +5,7 @@ use regex::Regex; use roxmltree::Node; use tracing::{error, warn}; -use crate::{constants::{BuildStatus, BuildSteps, OYarn, EXTENSION_NAME}, oyarn, threads::SessionInfo, S}; +use crate::{constants::{BuildStatus, BuildSteps, OYarn, EXTENSION_NAME}, core::entry_point::EntryPointType, oyarn, threads::SessionInfo, Sy, S}; use super::{file_mgr::FileInfo, odoo::SyncOdoo, symbols::{symbol::Symbol, xml_file_symbol::XmlFileSymbol}}; @@ -13,20 +13,93 @@ use super::{file_mgr::FileInfo, odoo::SyncOdoo, symbols::{symbol::Symbol, xml_fi Struct made to load RelaxNG Odoo schemas and add hooks and specific OdooLS behavior on particular nodes. */ pub struct XmlArchBuilder { + pub is_in_main_ep: bool, + pub xml_symbol: Rc>, } impl XmlArchBuilder { - pub fn new() -> Self { + pub fn new(xml_symbol: Rc>) -> Self { Self { + is_in_main_ep: false, + xml_symbol } } - pub fn load_arch(&mut self, session: &mut SessionInfo, xml_symbol: Rc>, file_info: &mut FileInfo, node: &Node) { + pub fn load_arch(&mut self, session: &mut SessionInfo, file_info: &mut FileInfo, node: &Node) { let mut diagnostics = vec![]; - xml_symbol.borrow_mut().set_build_status(BuildSteps::ARCH, BuildStatus::IN_PROGRESS); + self.xml_symbol.borrow_mut().set_build_status(BuildSteps::ARCH, BuildStatus::IN_PROGRESS); + let ep = self.xml_symbol.borrow().get_entry(); + if let Some(ep) = ep { + self.is_in_main_ep = ep.borrow().typ == EntryPointType::MAIN || ep.borrow().typ == EntryPointType::ADDON; + } self.load_odoo_openerp_data(session, node, &mut diagnostics); - xml_symbol.borrow_mut().set_build_status(BuildSteps::ARCH, BuildStatus::DONE); + self.xml_symbol.borrow_mut().set_build_status(BuildSteps::ARCH, BuildStatus::DONE); file_info.replace_diagnostics(BuildSteps::ARCH, diagnostics); } + + pub fn on_operation_creation( + &self, + session: &mut SessionInfo, + id: Option, + node: &Node, + diagnostics: &mut Vec, + ) { + if !self.is_in_main_ep { + return; + } + if let Some(id) = id { + let module = self.xml_symbol.borrow().find_module(); + if module.is_none() { + warn!("Module not found for id: {}", id); + return; + } + let module = module.unwrap(); + let id_split = id.split(".").collect::>(); + if id_split.len() > 2 { + diagnostics.push(Diagnostic::new( + Range { + start: Position::new(node.range().start as u32, 0), + end: Position::new(node.range().end as u32, 0), + }, + Some(DiagnosticSeverity::ERROR), + Some(lsp_types::NumberOrString::String(S!("OLS30446"))), + Some(EXTENSION_NAME.to_string()), + format!("Invalid XML ID '{}'. It should not contain more than one dot", id), + None, + None + )); + return; + } + let id = id_split.last().unwrap().to_string(); + let mut xml_module = module.clone(); + if id_split.len() == 2 { + let module_name = id_split.first().unwrap().to_string(); + if let Some(m) = session.sync_odoo.modules.get(&module_name) { + xml_module = m.upgrade().unwrap(); + } + } + let xml_module_bw = xml_module.borrow(); + let already_existing = xml_module_bw.as_module_package().xml_ids.get(&Sy!(id.clone())).cloned(); + drop(xml_module_bw); + let mut found_one = false; + if let Some(existing) = already_existing { + //Check that it exists a main xml_id + for s in existing.iter() { + if let Some(s) = s.upgrade() { + if Rc::ptr_eq(&s, &xml_module) { + found_one = true; + break; + } + } + } + } else { + xml_module.borrow_mut().as_module_package_mut().xml_ids.insert(Sy!(id.clone()), vec![]); + } + if !found_one && !Rc::ptr_eq(&xml_module, &module) { + // no diagnostic to create. + } + xml_module.borrow_mut().as_module_package_mut().xml_ids.get_mut(&Sy!(id)).unwrap().push(Rc::downgrade(&self.xml_symbol)); + } + } } \ No newline at end of file diff --git a/server/src/core/xml_arch_builder_rng_validation.rs b/server/src/core/xml_arch_builder_rng_validation.rs index d9ea022c..28a709dc 100644 --- a/server/src/core/xml_arch_builder_rng_validation.rs +++ b/server/src/core/xml_arch_builder_rng_validation.rs @@ -62,12 +62,12 @@ impl XmlArchBuilder { fn load_menuitem(&mut self, session: &mut SessionInfo, node: &Node, is_submenu: bool, diagnostics: &mut Vec) -> bool { if node.tag_name().name() != "menuitem" { return false; } - let mut found_id = false; + let mut found_id = None; let has_parent = node.attribute("parent").is_some(); for attr in node.attributes() { match attr.name() { "id" => { - found_id = true; + found_id = Some(attr.value().to_string()); }, "sequence" => { if attr.value().parse::().is_err() { @@ -133,7 +133,7 @@ impl XmlArchBuilder { } } } - if !found_id { + if found_id.is_none() { diagnostics.push(Diagnostic::new( Range { start: Position::new(node.range().start as u32, 0), end: Position::new(node.range().end as u32, 0) }, Some(DiagnosticSeverity::ERROR), @@ -158,16 +158,17 @@ impl XmlArchBuilder { self.load_menuitem(session, &child, true, diagnostics); } } + self.on_operation_creation(session, found_id, node, diagnostics); true } fn load_record(&mut self, session: &mut SessionInfo, node: &Node, diagnostics: &mut Vec) -> bool { if node.tag_name().name() != "record" { return false; } let mut found_model = false; - + let mut found_id = None; for attr in node.attributes() { match attr.name() { - "id" => {}, + "id" => {found_id = Some(attr.value().to_string());}, "forcecreate" => {}, "model" => {found_model = true;}, "uid" => {}, @@ -209,6 +210,7 @@ impl XmlArchBuilder { None)); } } + self.on_operation_creation(session, found_id, node, diagnostics); true } @@ -456,6 +458,8 @@ impl XmlArchBuilder { fn load_template(&mut self, session: &mut SessionInfo, node: &Node, diagnostics: &mut Vec) -> bool { if node.tag_name().name() != "template" { return false; } //no interesting rule to check, as 'any' is valid + let found_id = node.attribute("id").map(|s| s.to_string()); + self.on_operation_creation(session, found_id, node, diagnostics); true } @@ -471,9 +475,9 @@ impl XmlArchBuilder { None, None)); } - let has_id = node.attribute("id").is_some(); + let found_id = node.attribute("id").map(|s| s.to_string()); let has_search = node.attribute("search").is_some(); - if has_id && has_search { + if found_id.is_some() && has_search { diagnostics.push(Diagnostic::new( Range { start: Position::new(node.range().start as u32, 0), end: Position::new(node.range().end as u32, 0) }, Some(DiagnosticSeverity::ERROR), @@ -483,7 +487,7 @@ impl XmlArchBuilder { None, None)); } - if !has_id && !has_search { + if found_id.is_none() && !has_search { diagnostics.push(Diagnostic::new( Range { start: Position::new(node.range().start as u32, 0), end: Position::new(node.range().end as u32, 0) }, Some(DiagnosticSeverity::ERROR), @@ -493,11 +497,13 @@ impl XmlArchBuilder { None, None)); } + self.on_operation_creation(session, found_id, node, diagnostics); true } fn load_act_window(&mut self, session: &mut SessionInfo, node: &Node, diagnostics: &mut Vec) -> bool { if node.tag_name().name() != "act_window" { return false; } + let mut found_id = None; for attr in ["id", "name", "res_model"] { if node.attribute(attr).is_none() { diagnostics.push(Diagnostic::new( @@ -509,6 +515,9 @@ impl XmlArchBuilder { None, None)); } + if attr == "id" { + found_id = Some(node.attribute(attr).unwrap().to_string()); + } } for attr in node.attributes() { match attr.name() { @@ -562,11 +571,13 @@ impl XmlArchBuilder { None, None)); } + self.on_operation_creation(session, found_id, node, diagnostics); true } fn load_report(&mut self, session: &mut SessionInfo, node: &Node, diagnostics: &mut Vec) -> bool { if node.tag_name().name() != "report" { return false; } + let mut found_id = None; for attr in ["string", "model", "name"] { if node.attribute(attr).is_none() { diagnostics.push(Diagnostic::new( @@ -581,7 +592,8 @@ impl XmlArchBuilder { } for attr in node.attributes() { match attr.name() { - "id" | "print_report_name" | "report_type" | "multi"| "menu" | "keyword" | "file" | + "id" => { found_id = Some(attr.value().to_string()); }, + "print_report_name" | "report_type" | "multi"| "menu" | "keyword" | "file" | "xml" | "parser" | "auto" | "header" | "attachment" | "attachment_use" | "groups" | "paperformat" | "usage" => {}, _ => { diagnostics.push(Diagnostic::new( @@ -605,6 +617,7 @@ impl XmlArchBuilder { None, None)); } + self.on_operation_creation(session, found_id, node, diagnostics); true }