Skip to content

Commit 9958287

Browse files
committed
[IMP] server: check for field validity on xml record
1 parent 044bdf8 commit 9958287

File tree

8 files changed

+116
-61
lines changed

8 files changed

+116
-61
lines changed

server/src/core/model.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,26 +185,43 @@ impl Model {
185185

186186
/* Return all symbols that build this model.
187187
It returns the symbol and an optional string that represents the module name that should be added to dependencies to be used.
188+
if with_inheritance is true, it will also return symbols from inherited models (NOT Base classes).
188189
*/
189-
pub fn all_symbols(&self, session: &mut SessionInfo, from_module: Option<Rc<RefCell<Symbol>>>) -> Vec<(Rc<RefCell<Symbol>>, Option<OYarn>)> {
190-
let mut symbol = Vec::new();
190+
pub fn all_symbols(&self, session: &mut SessionInfo, from_module: Option<Rc<RefCell<Symbol>>>, with_inheritance: bool) -> Vec<(Rc<RefCell<Symbol>>, Option<OYarn>)> {
191+
self.all_symbols_helper(session, from_module, with_inheritance, &mut HashSet::new())
192+
}
193+
194+
fn all_symbols_helper(&self, session: &mut SessionInfo, from_module: Option<Rc<RefCell<Symbol>>>, with_inheritance: bool, seen_inherited_models: &mut HashSet<OYarn>) -> Vec<(Rc<RefCell<Symbol>>, Option<OYarn>)> {
195+
let mut symbols = Vec::new();
191196
for s in self.symbols.iter() {
192197
if let Some(from_module) = from_module.as_ref() {
193198
let module = s.borrow().find_module();
194199
if let Some(module) = module {
195200
if ModuleSymbol::is_in_deps(session, &from_module, &module.borrow().as_module_package().dir_name) {
196-
symbol.push((s, None));
201+
symbols.push((s.clone(), None));
197202
} else {
198-
symbol.push((s, Some(module.borrow().as_module_package().dir_name.clone())));
203+
symbols.push((s.clone(), Some(module.borrow().as_module_package().dir_name.clone())));
199204
}
200205
} else {
201206
session.log_message(MessageType::WARNING, "A model should be declared in a module.".to_string());
202207
}
203208
} else {
204-
symbol.push((s.clone(), None));
209+
symbols.push((s.clone(), None));
210+
}
211+
if with_inheritance {
212+
let inherited_models = s.borrow().as_class_sym()._model.as_ref().unwrap().inherit.clone();
213+
for inherited_model in inherited_models.iter() {
214+
if !seen_inherited_models.contains(inherited_model) {
215+
seen_inherited_models.insert(inherited_model.clone());
216+
let model = session.sync_odoo.models.get(inherited_model).cloned();
217+
if let Some(model) = model {
218+
symbols.extend(model.borrow().all_symbols_helper(session, from_module.clone(), true, seen_inherited_models));
219+
}
220+
}
221+
}
205222
}
206223
}
207-
symbol
224+
symbols
208225
}
209226

210227
pub fn add_dependent(&mut self, symbol: &Rc<RefCell<Symbol>>) {

server/src/core/python_arch_eval.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ impl PythonArchEval {
541541
let Some(model) = session.sync_odoo.models.get(&model_data.name).cloned() else {
542542
continue;
543543
};
544-
let model_classes = model.borrow().all_symbols(session, parent_class.find_module());
544+
let model_classes = model.borrow().all_symbols(session, parent_class.find_module(), false);
545545
let fn_name = self.sym_stack[0].borrow().name().clone();
546546
let allowed_fields: HashSet<_> = model_classes.iter().filter_map(|(sym, _)| sym.borrow().as_class_sym()._model.as_ref().unwrap().computes.get(&fn_name).cloned()).flatten().collect();
547547
if allowed_fields.is_empty() {

server/src/core/python_odoo_builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ impl PythonOdooBuilder {
9797
end: 1,
9898
}),
9999
xml_id: Some(xml_id_model_name),
100+
fields: vec![]
100101
}));
101102
});
102103
session.sync_odoo.models.insert(model_name.clone(), Rc::new(RefCell::new(model)));

server/src/core/symbols/symbol.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2295,7 +2295,7 @@ impl Symbol {
22952295
let model_data = sym.as_class_sym()._model.as_ref();
22962296
if let Some(model_data) = model_data {
22972297
if let Some(model) = session.sync_odoo.models.get(&model_data.name).cloned() {
2298-
for (model_sym, dependency) in model.borrow().all_symbols(session, from_module.clone()) {
2298+
for (model_sym, dependency) in model.borrow().all_symbols(session, from_module.clone(), true) {
22992299
if dependency.is_none() && !Rc::ptr_eq(symbol, &model_sym) {
23002300
for s in model_sym.borrow().all_symbols() {
23012301
if only_fields && !s.borrow().is_field(session){
@@ -2318,7 +2318,7 @@ impl Symbol {
23182318
}
23192319
let bases = symbol.borrow().as_class_sym().bases.clone();
23202320
for base in bases.iter() {
2321-
//no comodel as we will process only model in base class (overrided _name?)
2321+
//no comodel as we will search for co-model from original class (what about overrided _name?)
23222322
if let Some(base) = base.upgrade() {
23232323
Symbol::all_members(&base, session, result, false, only_fields, only_methods, from_module.clone(), acc, false);
23242324
}

server/src/core/xml_arch_builder_rng_validation.rs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
use std::rc::Rc;
2+
13
use lsp_types::{Diagnostic, Position, Range};
24
use once_cell::sync::Lazy;
35
use regex::Regex;
46
use roxmltree::Node;
57

6-
use crate::{core::diagnostics::{create_diagnostic, DiagnosticCode}};
7-
use crate::{constants::{BuildStatus, BuildSteps, OYarn, EXTENSION_NAME}, core::xml_data::{XmlData, XmlDataActWindow, XmlDataDelete, XmlDataMenuItem, XmlDataRecord, XmlDataReport, XmlDataTemplate}, oyarn, threads::SessionInfo, Sy, S};
8+
use crate::{constants::{BuildStatus, BuildSteps, OYarn, EXTENSION_NAME}, core::{diagnostics::{create_diagnostic, DiagnosticCode}, xml_data::{XmlData, XmlDataActWindow, XmlDataDelete, XmlDataField, XmlDataMenuItem, XmlDataRecord, XmlDataReport, XmlDataTemplate}}, oyarn, threads::SessionInfo, Sy, S};
89

910
use super::xml_arch_builder::XmlArchBuilder;
1011

@@ -213,9 +214,16 @@ impl XmlArchBuilder {
213214
}
214215
return false;
215216
}
216-
217+
let mut data = XmlDataRecord {
218+
file_symbol: Rc::downgrade(&self.xml_symbol),
219+
model: (oyarn!("{}", node.attribute("model").unwrap()), node.attribute_node("model").unwrap().range()),
220+
xml_id: found_id.clone().map(|id| oyarn!("{}", id)),
221+
fields: vec![]
222+
};
217223
for child in node.children().filter(|n| n.is_element()) {
218-
if !self.load_field(session, &child, diagnostics) {
224+
if let Some(field) = self.load_field(session, &child, diagnostics) {
225+
data.fields.push(field);
226+
} else {
219227
if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05015, &[child.tag_name().name()]) {
220228
diagnostics.push(Diagnostic {
221229
range: Range { start: Position::new(child.range().start as u32, 0), end: Position::new(child.range().end as u32, 0) },
@@ -224,17 +232,13 @@ impl XmlArchBuilder {
224232
}
225233
}
226234
}
227-
let data = XmlData::RECORD(XmlDataRecord {
228-
file_symbol: Rc::downgrade(&self.xml_symbol),
229-
model: (oyarn!("{}", node.attribute("model").unwrap()), node.attribute_node("model").unwrap().range()),
230-
xml_id: found_id.clone().map(|id| oyarn!("{}", id)),
231-
});
235+
let data = XmlData::RECORD(data);
232236
self.on_operation_creation(session, found_id, node, data, diagnostics);
233237
true
234238
}
235239

236-
fn load_field(&mut self, session: &mut SessionInfo, node: &Node, diagnostics: &mut Vec<Diagnostic>) -> bool {
237-
if node.tag_name().name() != "field" { return false; }
240+
fn load_field(&mut self, session: &mut SessionInfo, node: &Node, diagnostics: &mut Vec<Diagnostic>) -> Option<XmlDataField> {
241+
if node.tag_name().name() != "field" { return None; }
238242
if node.attribute("name").is_none() {
239243
if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS05016, &[]) {
240244
diagnostics.push(Diagnostic {
@@ -255,7 +259,7 @@ impl XmlArchBuilder {
255259
..diagnostic.clone()
256260
});
257261
}
258-
return false;
262+
return None;
259263
}
260264
let mut is_xml_or_html = false;
261265
if let Some(field_type) = node.attribute("type") {
@@ -365,7 +369,10 @@ impl XmlArchBuilder {
365369
}
366370
}
367371
}
368-
true
372+
Some(XmlDataField {
373+
name: oyarn!("{}", node.attribute("name").unwrap()),
374+
range: node.attribute_node("name").unwrap().range(),
375+
})
369376
}
370377

371378
fn load_value(&mut self, session: &mut SessionInfo, node: &Node, diagnostics: &mut Vec<Diagnostic>) -> bool {

server/src/core/xml_data.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ pub struct XmlDataRecord {
2020
pub file_symbol: Weak<RefCell<Symbol>>,
2121
pub model: (OYarn, Range<usize>),
2222
pub xml_id: Option<OYarn>,
23+
pub fields: Vec<XmlDataField>,
24+
}
25+
26+
#[derive(Debug, Clone)]
27+
pub struct XmlDataField {
28+
pub name: OYarn,
29+
pub range: Range<usize>,
2330
}
2431

2532
#[derive(Debug, Clone)]

server/src/core/xml_validation.rs

Lines changed: 61 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{cell::RefCell, collections::HashMap, hash::Hash, path::PathBuf, rc::Rc
33
use lsp_types::{Diagnostic, Position, Range};
44
use tracing::{info, trace};
55

6-
use crate::{constants::{BuildSteps, SymType, DEBUG_STEPS}, core::{entry_point::{EntryPoint, EntryPointType}, file_mgr::FileInfo, odoo::SyncOdoo, symbols::symbol::Symbol, xml_data::{XmlData, XmlDataActWindow, XmlDataDelete, XmlDataMenuItem, XmlDataRecord, XmlDataReport, XmlDataTemplate}}, threads::SessionInfo};
6+
use crate::{constants::{BuildSteps, SymType, DEBUG_STEPS, EXTENSION_NAME}, core::{entry_point::{EntryPoint, EntryPointType}, file_mgr::FileInfo, model::Model, odoo::SyncOdoo, symbols::symbol::Symbol, xml_data::{XmlData, XmlDataActWindow, XmlDataDelete, XmlDataMenuItem, XmlDataRecord, XmlDataReport, XmlDataTemplate}}, threads::SessionInfo, S};
77

88

99

@@ -35,74 +35,97 @@ impl XmlValidator {
3535
}
3636
let module = self.xml_symbol.borrow().find_module().unwrap();
3737
let mut dependencies = vec![];
38+
let mut model_dependencies = vec![];
39+
let mut diagnostics = vec![];
3840
for xml_ids in self.xml_symbol.borrow().as_xml_file_sym().xml_ids.values() {
3941
for xml_id in xml_ids.iter() {
40-
self.validate_xml_id(session, &module, xml_id, &mut dependencies);
42+
self.validate_xml_id(session, &module, xml_id, &mut diagnostics, &mut dependencies, &mut model_dependencies);
4143
}
4244
}
43-
for mut dep in dependencies.iter_mut() {
45+
for dep in dependencies.iter_mut() {
4446
self.xml_symbol.borrow_mut().add_dependency(&mut dep.borrow_mut(), BuildSteps::VALIDATION, BuildSteps::ARCH_EVAL);
4547
}
48+
for model in model_dependencies.iter() {
49+
self.xml_symbol.borrow_mut().add_model_dependencies(&model);
50+
}
4651
let file_info = self.get_file_info(&mut session.sync_odoo);
52+
file_info.borrow_mut().replace_diagnostics(BuildSteps::VALIDATION, diagnostics);
4753
file_info.borrow_mut().publish_diagnostics(session);
4854
}
4955

50-
pub fn validate_xml_id(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, data: &XmlData, dependencies: &mut Vec<Rc<RefCell<Symbol>>>) {
51-
let path = data.get_file_symbol().unwrap().upgrade().unwrap().borrow().paths()[0].clone();
52-
let mut file_info = session.sync_odoo.get_file_mgr().borrow().get_file_info(&path).unwrap();
53-
let mut diagnostics = vec![];
56+
pub fn validate_xml_id(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, data: &XmlData, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>, model_dependencies: &mut Vec<Rc<RefCell<Model>>>) {
57+
let Some(xml_file) = data.get_xml_file_symbol() else {
58+
return;
59+
};
60+
let path = xml_file.borrow().paths()[0].clone();
5461
match data {
55-
XmlData::RECORD(xml_data_record) => self.validate_record(session, module, xml_data_record, &mut diagnostics, dependencies),
56-
XmlData::MENUITEM(xml_data_menu_item) => self.validate_menu_item(session, module, xml_data_menu_item, &mut diagnostics, dependencies),
57-
XmlData::TEMPLATE(xml_data_template) => self.validate_template(session, module, xml_data_template, &mut diagnostics, dependencies),
58-
XmlData::DELETE(xml_data_delete) => self.validate_delete(session, module, xml_data_delete, &mut diagnostics, dependencies),
59-
XmlData::ACT_WINDOW(xml_data_act_window) => self.validate_act_window(session, module, xml_data_act_window, &mut diagnostics, dependencies),
60-
XmlData::REPORT(xml_data_report) => self.validate_report(session, module, xml_data_report, &mut diagnostics, dependencies),
62+
XmlData::RECORD(xml_data_record) => self.validate_record(session, module, xml_data_record, diagnostics, dependencies, model_dependencies),
63+
XmlData::MENUITEM(xml_data_menu_item) => self.validate_menu_item(session, module, xml_data_menu_item, diagnostics, dependencies, model_dependencies),
64+
XmlData::TEMPLATE(xml_data_template) => self.validate_template(session, module, xml_data_template, diagnostics, dependencies, model_dependencies),
65+
XmlData::DELETE(xml_data_delete) => self.validate_delete(session, module, xml_data_delete, diagnostics, dependencies, model_dependencies),
66+
XmlData::ACT_WINDOW(xml_data_act_window) => self.validate_act_window(session, module, xml_data_act_window, diagnostics, dependencies, model_dependencies),
67+
XmlData::REPORT(xml_data_report) => self.validate_report(session, module, xml_data_report, diagnostics, dependencies, model_dependencies),
6168
}
62-
file_info.borrow_mut().update_validation_diagnostics(HashMap::from([(BuildSteps::VALIDATION, diagnostics)]));
6369
}
6470

65-
fn validate_record(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, xml_data_record: &XmlDataRecord, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>) {
66-
let mut model_ok = false;
67-
let model = session.sync_odoo.models.get(&xml_data_record.model.0).cloned();
68-
if let Some(model) = model {
69-
self.xml_symbol.borrow_mut().add_model_dependencies(&model);
70-
for main_sym in model.borrow().get_main_symbols(session, Some(module.clone())).iter() {
71-
model_ok = true;
72-
dependencies.push(main_sym.borrow().get_file().unwrap().upgrade().unwrap());
73-
}
74-
} else {
71+
fn validate_record(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, xml_data_record: &XmlDataRecord, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>, model_dependencies: &mut Vec<Rc<RefCell<Model>>>) {
72+
let Some(model) = session.sync_odoo.models.get(&xml_data_record.model.0).cloned() else {
7573
//TODO register to not_found_models
76-
}
77-
if !model_ok {
78-
diagnostics.push(Diagnostic {
79-
range: Range::new(Position::new(xml_data_record.model.1.start.try_into().unwrap(), 0), Position::new(xml_data_record.model.1.end.try_into().unwrap(), 0)),
80-
severity: Some(lsp_types::DiagnosticSeverity::ERROR),
81-
message: format!("Model '{}' not found in module '{}'", xml_data_record.model.0, module.borrow().name()),
82-
source: Some("OdooLS".to_string()),
83-
..Default::default()
84-
});
74+
diagnostics.push(Diagnostic::new(
75+
Range::new(Position::new(xml_data_record.model.1.start.try_into().unwrap(), 0), Position::new(xml_data_record.model.1.end.try_into().unwrap(), 0)),
76+
Some(lsp_types::DiagnosticSeverity::ERROR),
77+
Some(lsp_types::NumberOrString::String(S!("OLS30450"))),
78+
Some(EXTENSION_NAME.to_string()),
79+
format!("Model '{}' not found in module '{}'", xml_data_record.model.0, module.borrow().name()),
80+
None,
81+
None
82+
));
8583
info!("Model '{}' not found in module '{}'", xml_data_record.model.0, module.borrow().name());
84+
return;
85+
};
86+
model_dependencies.push(model.clone());
87+
let main_symbols = model.borrow().get_main_symbols(session, Some(module.clone()));
88+
for main_sym in main_symbols.iter() {
89+
dependencies.push(main_sym.borrow().get_file().unwrap().upgrade().unwrap());
90+
}
91+
let mut all_fields = HashMap::new();
92+
Symbol::all_members(&main_symbols[0], session, &mut all_fields, true, true, false, Some(module.clone()), &mut None, false);
93+
for field in &xml_data_record.fields {
94+
let declared_field = all_fields.get(&field.name);
95+
if let Some(declared_field) = declared_field {
96+
//TODO Check type
97+
} else {
98+
99+
diagnostics.push(Diagnostic::new(
100+
Range::new(Position::new(field.range.start.try_into().unwrap(), 0), Position::new(field.range.end.try_into().unwrap(), 0)),
101+
Some(lsp_types::DiagnosticSeverity::ERROR),
102+
Some(lsp_types::NumberOrString::String(S!("OLS30451"))),
103+
Some(EXTENSION_NAME.to_string()),
104+
format!("Field '{}' not found in model '{}'", field.name, xml_data_record.model.0),
105+
None,
106+
None
107+
));
108+
}
86109
}
87110
}
88111

89-
fn validate_menu_item(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, xml_data_menu_item: &XmlDataMenuItem, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>) {
112+
fn validate_menu_item(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, xml_data_menu_item: &XmlDataMenuItem, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>, model_dependencies: &mut Vec<Rc<RefCell<Model>>>) {
90113

91114
}
92115

93-
fn validate_template(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, xml_data_template: &XmlDataTemplate, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>) {
116+
fn validate_template(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, xml_data_template: &XmlDataTemplate, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>, model_dependencies: &mut Vec<Rc<RefCell<Model>>>) {
94117

95118
}
96119

97-
fn validate_delete(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, xml_data_delete: &XmlDataDelete, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>) {
120+
fn validate_delete(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, xml_data_delete: &XmlDataDelete, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>, model_dependencies: &mut Vec<Rc<RefCell<Model>>>) {
98121

99122
}
100123

101-
fn validate_act_window(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, xml_data_act_window: &XmlDataActWindow, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>) {
124+
fn validate_act_window(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, xml_data_act_window: &XmlDataActWindow, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>, model_dependencies: &mut Vec<Rc<RefCell<Model>>>) {
102125

103126
}
104127

105-
fn validate_report(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, xml_data_report: &XmlDataReport, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>) {
128+
fn validate_report(&self, session: &mut SessionInfo, module: &Rc<RefCell<Symbol>>, xml_data_report: &XmlDataReport, diagnostics: &mut Vec<Diagnostic>, dependencies: &mut Vec<Rc<RefCell<Symbol>>>, model_dependencies: &mut Vec<Rc<RefCell<Model>>>) {
106129

107130
}
108131
}

0 commit comments

Comments
 (0)