Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions server/src/core/diagnostic_codes_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,18 @@ OLS03019, DiagnosticSetting::Error, "Compute method not set to modify this field
* Hence, this model is shadowing an existing model.
*/
OLS03020, DiagnosticSetting::Warning, "Model {0} is shadowing an existing model in dependencies",
/**
* On a One2many field, the inverse_name should be a field on the comodel that is a Many2one to the current model.
*/
OLS03021, DiagnosticSetting::Error, "Inverse field {0} does not exist on comodel {1}",
/**
* On a One2many field, the inverse_name should be a field on the comodel that is a Many2one to the current model.
*/
OLS03022, DiagnosticSetting::Error, "Inverse field is not a Many2one field",
/**
* On a One2many field, the inverse_name should be a field on the comodel that is a Many2one to the current model.
*/
OLS03023, DiagnosticSetting::Error, "Inverse field {0} is not pointing to the current model {1}, but rather to {2}",
/**
* A __manifest__.py file should be evaluated with a literal_eval to a single dictionary.
* Do not store any other information in it.
Expand Down
7 changes: 4 additions & 3 deletions server/src/core/evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,7 @@ impl Evaluation {
}
}
//1: find __init__ method
let init = base_sym.borrow().get_member_symbol(session, &S!("__init__"), module.clone(), true, false, false, false);
let init = base_sym.borrow().get_member_symbol(session, &S!("__init__"), module.clone(), true, false, false, false, false);
let mut found_hook = false;
if let Some(init) = init.0.first() {
if let Some(init_eval) = init.borrow().evaluations() {
Expand Down Expand Up @@ -1039,7 +1039,7 @@ impl Evaluation {
}
}
let is_super = ibase.is_weak() && ibase.as_weak().is_super;
let (attributes, mut attributes_diagnostics) = base_loc.borrow().get_member_symbol(session, &expr.attr.to_string(), module.clone(), false, false, true, is_super);
let (attributes, mut attributes_diagnostics) = base_loc.borrow().get_member_symbol(session, &expr.attr.to_string(), module.clone(), false, false, false, true, is_super);
for diagnostic in attributes_diagnostics.iter_mut(){
diagnostic.range = FileMgr::textRange_to_temporary_Range(&expr.range())
}
Expand Down Expand Up @@ -1265,7 +1265,7 @@ impl Evaluation {
for base_eval_ptr in base_eval_ptrs.iter() {
let EvaluationSymbolPtr::WEAK(base_sym_weak_eval) = base_eval_ptr else {continue};
let Some(base_sym) = base_sym_weak_eval.weak.upgrade() else {continue};
let (operator_functions, diags) = base_sym.borrow().get_member_symbol(session, &S!(method), module.clone(), true, false, false, false);
let (operator_functions, diags) = base_sym.borrow().get_member_symbol(session, &S!(method), module.clone(), true, false, true, false, false);
diagnostics.extend(diags);
for operator_function in operator_functions.into_iter(){
for eval in operator_function.borrow().evaluations().unwrap_or(&vec![]).iter() {
Expand Down Expand Up @@ -1569,6 +1569,7 @@ impl Evaluation {
false,
true,
false,
false,
false);
if symbols.is_empty() {
if let Some(diagnostic_base) = create_diagnostic(session, DiagnosticCode::OLS03011, &[&name, &object.borrow().name()]) {
Expand Down
3 changes: 2 additions & 1 deletion server/src/core/python_arch_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ impl PythonArchEval {
let symbol_type_rc = symbol_eval[0].upgrade_weak().unwrap();
let symbol_type = symbol_type_rc.borrow();
if symbol_type.typ() == SymType::CLASS {
let (iter, _) = symbol_type.get_member_symbol(session, &S!("__iter__"), None, true, false, false, false);
let (iter, _) = symbol_type.get_member_symbol(session, &S!("__iter__"), None, true, false, false, false, false);
if iter.len() == 1 {
SyncOdoo::build_now(session, &iter[0], BuildSteps::ARCH_EVAL);
SyncOdoo::build_now(session, &iter[0], BuildSteps::VALIDATION);
Expand Down Expand Up @@ -1030,6 +1030,7 @@ impl PythonArchEval {
from_module.clone(),
false,
true,
false,
true,
false);
if ix == split_expr.len() - 1 {
Expand Down
3 changes: 2 additions & 1 deletion server/src/core/python_arch_eval_hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,7 @@ impl PythonArchEvalHooks {
("comodel_name", "str"),
("related", "str"),
("compute", "str"),
("inverse_name", "str"),
("delegate", "bool"),
("required", "bool"),
("default", "bool"),
Expand Down Expand Up @@ -1125,7 +1126,7 @@ impl PythonArchEvalHooks {
for arg in arguments.args.iter() {
let Expr::StringLiteral(expr) = arg else {return diagnostics};
let field_name = expr.value.to_string();
let (syms, _) = class_sym.borrow().get_member_symbol(session, &field_name, from_module.clone(), false, false, true, false);
let (syms, _) = class_sym.borrow().get_member_symbol(session, &field_name, from_module.clone(), false, true, false, true, false);
if syms.is_empty(){
if let Some(diagnostic) = create_diagnostic(session, DiagnosticCode::OLS03014, &[&field_name, &model_name]) {
diagnostics.push(Diagnostic {
Expand Down
2 changes: 1 addition & 1 deletion server/src/core/python_odoo_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ impl PythonOdooBuilder {
}

fn _get_attribute(session: &mut SessionInfo, loc_sym: &mut Symbol, attr: &String, diagnostics: &mut Vec<Diagnostic>) -> Option<EvaluationValue> {
let (attr_sym, _) = loc_sym.get_member_symbol(session, attr, None, true, false, false, false);
let (attr_sym, _) = loc_sym.get_member_symbol(session, attr, None, true, false, false, false, false);
if attr_sym.len() == 0 {
return None;
}
Expand Down
83 changes: 79 additions & 4 deletions server/src/core/python_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::path::PathBuf;
use lsp_types::{Diagnostic, Position, Range};
use crate::core::diagnostics::{create_diagnostic, DiagnosticCode};
use crate::core::evaluation::ContextValue;
use crate::{constants::*, Sy};
use crate::{constants::*, oyarn, Sy};
use crate::core::symbols::symbol::Symbol;
use crate::core::odoo::SyncOdoo;
use crate::core::symbols::module_symbol::ModuleSymbol;
Expand Down Expand Up @@ -422,7 +422,7 @@ impl PythonValidator {
}
let Some(field_type) = symbol
.borrow()
.get_member_symbol(session, &S!("type"), None, false, false, false, false)
.get_member_symbol(session, &S!("type"), None, false, false, false, false, false)
.0.first()
.and_then(|field_type_var| field_type_var.borrow().evaluations().cloned())
.and_then(|evals| evals.first().cloned())
Expand All @@ -445,7 +445,7 @@ impl PythonValidator {
};
let found = related_field_class_sym
.borrow()
.get_member_symbol(session, &S!("type"), None, false, false, false, false)
.get_member_symbol(session, &S!("type"), None, false, false, false, false, false)
.0.first()
.and_then(|field_type_var| field_type_var.borrow().evaluations().cloned())
.and_then(|evals| evals.first().cloned())
Expand Down Expand Up @@ -507,9 +507,10 @@ impl PythonValidator {
false,
false,
true,
true,
false
);
let method_found = symbols.iter().any(|symbol| symbol.borrow().typ() == SymType::FUNCTION);
let method_found = !symbols.is_empty();
if !method_found{
let Some(arg_range) = eval_weak.as_weak().context.get(&format!("{special_fn_field_name}_arg_range")).map(|ctx_val| ctx_val.as_text_range()) else {
continue;
Expand All @@ -523,6 +524,80 @@ impl PythonValidator {

}
}
if let Some(inverse_name) = eval_weak.as_weak().context.get(&S!("inverse_name")).map(|ctx_val| ctx_val.as_string()) {
let Some(model_name) = eval_weak.as_weak().context.get(&S!("comodel_name")).map(|ctx_val| ctx_val.as_string()) else {
continue;
};
let Some(model) = session.sync_odoo.models.get(&oyarn!("{}", model_name)).cloned() else {
continue;
};
let Some(module) = class_ref.find_module() else {
continue;
};
let main_syms = model.borrow().get_main_symbols(session, Some(module.clone()));
let symbols: Vec<_> = main_syms.iter().flat_map(|main_sym|
main_sym.clone().borrow().get_member_symbol(session, &inverse_name, Some(module.clone()), false, true, false, true, false).0
).collect();
let method_found = !symbols.is_empty();
if !method_found{
let Some(arg_range) = eval_weak.as_weak().context.get(&format!("inverse_name_arg_range")).map(|ctx_val| ctx_val.as_text_range()) else {
continue;
};
if let Some(diagnostic_base) = create_diagnostic(&session, DiagnosticCode::OLS03021, &[&inverse_name, &model_name]) {
self.diagnostics.push(Diagnostic {
range: Range::new(Position::new(arg_range.start().to_u32(), 0), Position::new(arg_range.end().to_u32(), 0)),
..diagnostic_base.clone()
});
}
}
if symbols.iter().any(|sym| !sym.borrow().is_specific_field(session, &["Many2one"])) {
let Some(arg_range) = eval_weak.as_weak().context.get(&format!("inverse_name_arg_range")).map(|ctx_val| ctx_val.as_text_range()) else {
continue;
};
if let Some(diagnostic_base) = create_diagnostic(&session, DiagnosticCode::OLS03022, &[]) {
self.diagnostics.push(Diagnostic {
range: Range::new(Position::new(arg_range.start().to_u32(), 0), Position::new(arg_range.end().to_u32(), 0)),
..diagnostic_base.clone()
});
}
} else {
// Check if we have a many2one field pointing to the comodel with another name than the current model
let mut comodel_eval_weaks = Vec::new();
for sym in symbols.iter() {
let sym_ref = sym.borrow();
let evals = sym_ref.evaluations().as_ref().unwrap().iter();
for eval in evals {
let followed = Symbol::follow_ref(
&eval.symbol.get_symbol(session, &mut None, &mut vec![], None),
session,
&mut None,
true,
false,
None,
);
comodel_eval_weaks.extend(followed);
}
}
for comodel_eval_weak in comodel_eval_weaks {
let Some(model_name) = comodel_eval_weak.as_weak().context.get(&S!("comodel_name")).map(|ctx_val| ctx_val.as_string()) else {
continue;
};
if model_name == model_data.name { // valid
continue;
}
let Some(arg_range) = eval_weak.as_weak().context.get(&format!("inverse_name_arg_range")).map(|ctx_val| ctx_val.as_text_range()) else {
continue;
};
if let Some(diagnostic_base) = create_diagnostic(&session, DiagnosticCode::OLS03023, &[&inverse_name, &model_data.name, &model_name]) {
self.diagnostics.push(Diagnostic {
range: Range::new(Position::new(arg_range.start().to_u32(), 0), Position::new(arg_range.end().to_u32(), 0)),
..diagnostic_base.clone()
});
break;
}
}
}
}
}
}
}
Expand Down
38 changes: 31 additions & 7 deletions server/src/core/symbols/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2123,7 +2123,7 @@ impl Symbol {
if let Some(base_attr) = base_attr {
let attribute_type_sym = symbol;
//TODO shouldn't we set the from_module in the call to get_member_symbol?
let get_method = attribute_type_sym.get_member_symbol(session, &S!("__get__"), None, true, false, true, false).0.first().cloned();
let get_method = attribute_type_sym.get_member_symbol(session, &S!("__get__"), None, true, false, false, true, false).0.first().cloned();
match get_method {
Some(get_method) if (base_attr.borrow().typ() == SymType::CLASS) => {
let get_method = get_method.borrow();
Expand Down Expand Up @@ -2746,12 +2746,33 @@ impl Symbol {
if not all, it will return the first found. If all, the all found symbols are returned, but the first one
is the one that is overriding others.
:param: from_module: optional, can change the from_module of the given class */
pub fn get_member_symbol(&self, session: &mut SessionInfo, name: &String, from_module: Option<Rc<RefCell<Symbol>>>, prevent_comodel: bool, only_fields: bool, all: bool, is_super: bool) -> (Vec<Rc<RefCell<Symbol>>>, Vec<Diagnostic>) {
pub fn get_member_symbol(
&self,
session: &mut SessionInfo,
name: &String,
from_module: Option<Rc<RefCell<Symbol>>>,
prevent_comodel: bool,
only_fields: bool,
only_methods: bool,
all: bool,
is_super: bool
) -> (Vec<Rc<RefCell<Symbol>>>, Vec<Diagnostic>) {
let mut visited_classes: PtrWeakHashSet<Weak<RefCell<Symbol>>> = PtrWeakHashSet::new();
return self._get_member_symbol_helper(session, name, from_module, prevent_comodel, only_fields, all, is_super, &mut visited_classes);
return self._get_member_symbol_helper(session, name, from_module, prevent_comodel, only_fields, only_methods, all, is_super, &mut visited_classes);
}

fn _get_member_symbol_helper(&self, session: &mut SessionInfo, name: &String, from_module: Option<Rc<RefCell<Symbol>>>, prevent_comodel: bool, only_fields: bool, all: bool, is_super: bool, visited_classes: &mut PtrWeakHashSet<Weak<RefCell<Symbol>>>) -> (Vec<Rc<RefCell<Symbol>>>, Vec<Diagnostic>) {
fn _get_member_symbol_helper(
&self,
session: &mut SessionInfo,
name: &String,
from_module: Option<Rc<RefCell<Symbol>>>,
prevent_comodel: bool,
only_fields: bool,
only_methods: bool,
all: bool,
is_super: bool,
visited_classes: &mut PtrWeakHashSet<Weak<RefCell<Symbol>>>
) -> (Vec<Rc<RefCell<Symbol>>>, Vec<Diagnostic>) {
let mut result: Vec<Rc<RefCell<Symbol>>> = vec![];
let mut visited_symbols: PtrWeakHashSet<Weak<RefCell<Symbol>>> = PtrWeakHashSet::new();
let mut extend_result = |syms: Vec<Rc<RefCell<Symbol>>>| {
Expand Down Expand Up @@ -2779,6 +2800,9 @@ impl Symbol {
if only_fields {
content_syms = content_syms.iter().filter(|x| x.borrow().is_field(session)).cloned().collect();
}
if only_methods {
content_syms = content_syms.iter().filter(|x| x.borrow().typ() == SymType::FUNCTION).cloned().collect();
}
if !content_syms.is_empty() {
if all {
extend_result(content_syms);
Expand All @@ -2801,7 +2825,7 @@ impl Symbol {
continue;
}
visited_classes.insert(model_symbol.clone());
let (attributs, att_diagnostic) = model_symbol.borrow()._get_member_symbol_helper(session, name, None, true, only_fields, all, false, visited_classes);
let (attributs, att_diagnostic) = model_symbol.borrow()._get_member_symbol_helper(session, name, None, true, only_fields, only_methods, all, false, visited_classes);
diagnostics.extend(att_diagnostic);
if all {
extend_result(attributs);
Expand All @@ -2819,7 +2843,7 @@ impl Symbol {
continue;
}
visited_classes.insert(model_symbol.clone());
let (attributs, att_diagnostic) = model_symbol.borrow()._get_member_symbol_helper(session, name, None, true, true, all, false, visited_classes);
let (attributs, att_diagnostic) = model_symbol.borrow()._get_member_symbol_helper(session, name, None, true, true, only_methods, all, false, visited_classes);
diagnostics.extend(att_diagnostic);
if all {
extend_result(attributs);
Expand All @@ -2843,7 +2867,7 @@ impl Symbol {
continue;
}
visited_classes.insert(base.clone());
let (s, s_diagnostic) = base.borrow().get_member_symbol(session, name, from_module.clone(), prevent_comodel, only_fields, all, false);
let (s, s_diagnostic) = base.borrow().get_member_symbol(session, name, from_module.clone(), prevent_comodel, only_fields, only_methods, all, false);
diagnostics.extend(s_diagnostic);
if !s.is_empty() {
if all {
Expand Down
Loading