Skip to content

Commit 2c4563c

Browse files
mmahroussfda-odoo
authored andcommitted
[FIX] server: follow ref for decs and version check
1 parent 16dcdc4 commit 2c4563c

File tree

5 files changed

+110
-43
lines changed

5 files changed

+110
-43
lines changed

server/src/core/python_arch_eval_hooks.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -560,9 +560,17 @@ impl PythonArchEvalHooks {
560560
let (dec_evals, diags) = Evaluation::eval_from_ast(session, &decorator_base, parent, &func_stmt.range.start(),&mut deps);
561561
Symbol::insert_dependencies(&file, &mut deps, current_step);
562562
diagnostics.extend(diags);
563-
for decorator_eval in dec_evals.iter(){
564-
let EvaluationSymbolPtr::WEAK(decorator_eval_sym_weak) = decorator_eval.symbol.get_symbol(session, &mut None, &mut diagnostics, None) else {continue};
565-
let Some(dec_sym) = decorator_eval_sym_weak.weak.upgrade() else {continue};
563+
let mut followed_evals = vec![];
564+
for eval in dec_evals {
565+
followed_evals.extend(Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, &mut vec![], None), session, &mut None, true, false, None, &mut vec![]));
566+
}
567+
for decorator_eval in followed_evals {
568+
let EvaluationSymbolPtr::WEAK(decorator_eval_sym_weak) = decorator_eval else {
569+
continue;
570+
};
571+
let Some(dec_sym) = decorator_eval_sym_weak.weak.upgrade() else {
572+
continue;
573+
};
566574
let dec_sym_tree = dec_sym.borrow().get_tree();
567575
for hook in arch_eval_decorator_hooks.iter() {
568576
for (min_version, max_version, hook_tree) in hook.trees.iter() {

server/src/features/completion.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::cmp::Ordering;
12
use std::collections::{HashMap, HashSet};
23
use std::{cell::RefCell, rc::Rc};
34
use itertools::Itertools;
@@ -12,6 +13,7 @@ use crate::core::odoo::SyncOdoo;
1213
use crate::core::python_arch_eval_hooks::PythonArchEvalHooks;
1314
use crate::core::symbols::module_symbol::ModuleSymbol;
1415
use crate::threads::SessionInfo;
16+
use crate::utils::compare_semver;
1517
use crate::{oyarn, Sy, S};
1618
use crate::core::symbols::symbol::Symbol;
1719
use crate::features::features_utils::FeaturesUtils;
@@ -504,12 +506,22 @@ fn complete_decorator_call(
504506
}
505507
let scope = Symbol::get_scope_symbol(file.clone(), offset as u32, false);
506508
let dec_evals = Evaluation::eval_from_ast(session, &decorator_base, scope.clone(), max_infer, &mut vec![]).0;
507-
for decorator_eval in dec_evals.iter(){
508-
let EvaluationSymbolPtr::WEAK(decorator_eval_sym_weak) = decorator_eval.symbol.get_symbol(session, &mut None, &mut vec![], None) else {continue};
509-
let Some(dec_sym) = decorator_eval_sym_weak.weak.upgrade() else {continue};
509+
let mut followed_evals = vec![];
510+
for eval in dec_evals {
511+
followed_evals.extend(Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, &mut vec![], None), session, &mut None, true, false, None, &mut vec![]));
512+
}
513+
for decorator_eval in followed_evals{
514+
let EvaluationSymbolPtr::WEAK(decorator_eval_sym_weak) = decorator_eval else {
515+
continue;
516+
};
517+
let Some(dec_sym) = decorator_eval_sym_weak.weak.upgrade() else {
518+
continue;
519+
};
510520
let dec_sym_tree = dec_sym.borrow().get_tree();
511-
let expected_types = if dec_sym_tree.0.ends_with(&[Sy!("odoo"), Sy!("api")]){
512-
if [vec![Sy!("onchange")], vec![Sy!("constrains")]].contains(&dec_sym_tree.1) && SyncOdoo::is_in_main_entry(session, &dec_sym_tree.0){
521+
let version_comparison = compare_semver(session.sync_odoo.full_version.as_str(), "18.1.0");
522+
let expected_types = if (version_comparison < Ordering::Equal && dec_sym_tree.0.ends_with(&[Sy!("odoo"), Sy!("api")])) ||
523+
(version_comparison >= Ordering::Equal && dec_sym_tree.0.ends_with(&[Sy!("odoo"), Sy!("orm"), Sy!("decorators")])) {
524+
if [vec![Sy!("onchange")], vec![Sy!("constrains")]].contains(&dec_sym_tree.1) && SyncOdoo::is_in_main_entry(session, &dec_sym_tree.0) {
513525
&vec![ExpectedType::SIMPLE_FIELD]
514526
} else if dec_sym_tree.1 == vec![Sy!("depends")] && SyncOdoo::is_in_main_entry(session, &dec_sym_tree.0){
515527
&vec![ExpectedType::NESTED_FIELD(None)]

server/src/features/features_utils.rs

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use ruff_text_size::{Ranged, TextRange, TextSize};
44
use crate::core::file_mgr::FileMgr;
55
use crate::core::odoo::SyncOdoo;
66
use crate::core::symbols::function_symbol::Argument;
7-
use crate::utils::PathSanitizer;
7+
use crate::utils::{compare_semver, PathSanitizer};
8+
use std::cmp::Ordering;
89
use std::collections::HashMap;
910
use std::path::PathBuf;
1011
use std::rc::Weak;
@@ -71,12 +72,13 @@ impl FeaturesUtils {
7172
return vec![];
7273
}
7374
let evaluations = Evaluation::eval_from_ast(session, &call_expr.func, scope.clone(), &call_expr.func.range().start(), &mut vec![]).0;
74-
if !evaluations.iter().any(|eval|
75-
match eval.symbol.get_symbol_as_weak(session, &mut None, &mut vec![], None).weak.upgrade() {
76-
Some(sym_rc) => sym_rc.borrow().is_field_class(session),
77-
None => false
78-
}
79-
){
75+
let mut followed_evals = vec![];
76+
for eval in evaluations {
77+
followed_evals.extend(Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, &mut vec![], None), session, &mut None, true, false, None, &mut vec![]));
78+
}
79+
if !followed_evals.iter().any(|eval|
80+
eval.is_weak() && eval.as_weak().weak.upgrade().map(|sym| sym.borrow().is_field_class(session)).unwrap_or(false)
81+
) {
8082
return vec![];
8183
}
8284
parent_class.clone().borrow().get_member_symbol(session, field_value, from_module.clone(), false, false, true, false).0
@@ -188,10 +190,16 @@ impl FeaturesUtils {
188190
) -> Vec<Rc<RefCell<Symbol>>>{
189191
let mut arg_symbols: Vec<Rc<RefCell<Symbol>>> = vec![];
190192
let callable_evals = Evaluation::eval_from_ast(session, &call_expr.func, scope.clone(), &call_expr.func.range().start(), &mut vec![]).0;
191-
for callable_eval in callable_evals.iter() {
192-
let callable = callable_eval.symbol.get_symbol_as_weak(session, &mut None, &mut vec![], None);
193+
let mut followed_evals = vec![];
194+
for eval in callable_evals {
195+
followed_evals.extend(Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, &mut vec![], None), session, &mut None, true, false, None, &mut vec![]));
196+
}
197+
for callable_eval in followed_evals {
198+
let EvaluationSymbolPtr::WEAK(callable) = callable_eval else {
199+
continue;
200+
};
193201
let Some(callable_sym) = callable.weak.upgrade() else {
194-
continue
202+
continue;
195203
};
196204
if callable_sym.borrow().typ() != SymType::FUNCTION {
197205
continue;
@@ -200,18 +208,21 @@ impl FeaturesUtils {
200208

201209
// Check if we are in api.onchange/constrains/depends
202210
let func_sym_tree = func.get_tree();
203-
if func_sym_tree.0.ends_with(&[Sy!("odoo"), Sy!("api")]){
204-
if [vec![Sy!("onchange")], vec![Sy!("constrains")]].contains(&func_sym_tree.1) && SyncOdoo::is_in_main_entry(session, &func_sym_tree.0){
205-
arg_symbols.extend(
206-
FeaturesUtils::find_simple_decorator_field_symbol(session, scope.clone(), from_module.clone(), field_name)
207-
);
208-
continue;
209-
} else if func_sym_tree.1 == vec![Sy!("depends")] && SyncOdoo::is_in_main_entry(session, &func_sym_tree.0){
210-
arg_symbols.extend(
211-
FeaturesUtils::find_nested_fields_in_class(session, scope.clone(), from_module.clone(), &field_range, field_name, &offset)
212-
);
213-
continue;
214-
}
211+
// TODO: account for change in tree after 18.1 odoo.orm.decorators
212+
let version_comparison = compare_semver(session.sync_odoo.full_version.as_str(), "18.1.0");
213+
if (version_comparison < Ordering::Equal && func_sym_tree.0.ends_with(&[Sy!("odoo"), Sy!("api")])) ||
214+
(version_comparison >= Ordering::Equal && func_sym_tree.0.ends_with(&[Sy!("odoo"), Sy!("orm"), Sy!("decorators")])){
215+
if [vec![Sy!("onchange")], vec![Sy!("constrains")]].contains(&func_sym_tree.1) && SyncOdoo::is_in_main_entry(session, &func_sym_tree.0) {
216+
arg_symbols.extend(
217+
FeaturesUtils::find_simple_decorator_field_symbol(session, scope.clone(), from_module.clone(), field_name)
218+
);
219+
continue;
220+
} else if func_sym_tree.1 == vec![Sy!("depends")] && SyncOdoo::is_in_main_entry(session, &func_sym_tree.0){
221+
arg_symbols.extend(
222+
FeaturesUtils::find_nested_fields_in_class(session, scope.clone(), from_module.clone(), &field_range, field_name, &offset)
223+
);
224+
continue;
225+
}
215226
}
216227

217228
let func_arg = func.as_func().get_indexed_arg_in_call(
@@ -251,10 +262,16 @@ impl FeaturesUtils {
251262
}
252263
let mut arg_symbols: Vec<Rc<RefCell<Symbol>>> = vec![];
253264
let callable_evals = Evaluation::eval_from_ast(session, &call_expr.func, scope.clone(), &call_expr.func.range().start(), &mut vec![]).0;
254-
for callable_eval in callable_evals.iter() {
255-
let callable = callable_eval.symbol.get_symbol_as_weak(session, &mut None, &mut vec![], None);
265+
let mut followed_evals = vec![];
266+
for eval in callable_evals {
267+
followed_evals.extend(Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, &mut vec![], None), session, &mut None, true, false, None, &mut vec![]));
268+
}
269+
for callable_eval in followed_evals {
270+
let EvaluationSymbolPtr::WEAK(callable) = callable_eval else {
271+
continue;
272+
};
256273
let Some(callable_sym) = callable.weak.upgrade() else {
257-
continue
274+
continue;
258275
};
259276
if !callable_sym.borrow().is_field_class(session) {
260277
continue;

server/tests/test_get_symbol.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ fn test_hover_on_model_field_and_method() {
2020
assert!(PathBuf::from(&test_file).exists(), "Test file does not exist: {}", test_file);
2121
let mut session = setup::setup::create_session(&mut odoo);
2222

23+
// Use Lazy value for partner and country class names
24+
let partner_class_name = test_utils::PARTNER_CLASS_NAME(session.sync_odoo.full_version.as_str());
25+
let country_class_name = test_utils::COUNTRY_CLASS_NAME(session.sync_odoo.full_version.as_str());
2326
// Get file symbol and file info
2427
let file_mgr = session.sync_odoo.get_file_mgr();
2528
let file_info = file_mgr.borrow().get_file_info(&test_file).unwrap();
@@ -53,12 +56,12 @@ fn test_hover_on_model_field_and_method() {
5356
// Hover on related field "partner_company_phone_code"
5457
let hover_partner_id = test_utils::get_hover_markdown(&mut session, &file_symbol, &file_info, 10, 63).unwrap_or_default();
5558
assert!(
56-
hover_partner_id.contains("partner_id: Partner"),
59+
hover_partner_id.contains(&format!("partner_id: {}", partner_class_name)),
5760
"Hover on field_name in related field name should show field name and field type"
5861
);
5962
let hover_country_id = test_utils::get_hover_markdown(&mut session, &file_symbol, &file_info, 10, 74).unwrap_or_default();
6063
assert!(
61-
hover_country_id.contains("country_id: Country"),
64+
hover_country_id.contains(&format!("country_id: {}", country_class_name)),
6265
"Hover on field_name in related field name should show field name and field type"
6366
);
6467
let hover_phone_code = test_utils::get_hover_markdown(&mut session, &file_symbol, &file_info, 10, 86).unwrap_or_default();
@@ -96,12 +99,12 @@ fn test_hover_on_model_field_and_method() {
9699
// Hover on depends decorator, on different sections
97100
let hover_partner_id = test_utils::get_hover_markdown(&mut session, &file_symbol, &file_info, 27, 22).unwrap_or_default();
98101
assert!(
99-
hover_partner_id.contains("partner_id: Partner"),
102+
hover_partner_id.contains(&format!("partner_id: {}", partner_class_name)),
100103
"Hover on field_name in depends should show field name and field type"
101104
);
102105
let hover_country_id = test_utils::get_hover_markdown(&mut session, &file_symbol, &file_info, 27, 35).unwrap_or_default();
103106
assert!(
104-
hover_country_id.contains("country_id: Country"),
107+
hover_country_id.contains(&format!("country_id: {}", country_class_name)),
105108
"Hover on field_name in depends should show field name and field type"
106109
);
107110
let hover_code = test_utils::get_hover_markdown(&mut session, &file_symbol, &file_info, 27, 43).unwrap_or_default();
@@ -125,12 +128,12 @@ fn test_hover_on_model_field_and_method() {
125128
// Hover on domains, on different sections
126129
let hover_partner_id = test_utils::get_hover_markdown(&mut session, &file_symbol, &file_info, 31, 25).unwrap_or_default();
127130
assert!(
128-
hover_partner_id.contains("partner_id: Partner"),
131+
hover_partner_id.contains(&format!("partner_id: {}", partner_class_name)),
129132
"Hover on field_name in search domain should show field name and field type"
130133
);
131134
let hover_country_id = test_utils::get_hover_markdown(&mut session, &file_symbol, &file_info, 31, 39).unwrap_or_default();
132135
assert!(
133-
hover_country_id.contains("country_id: Country"),
136+
hover_country_id.contains(&format!("country_id: {}", country_class_name)),
134137
"Hover on field_name in search domain should show field name and field type"
135138
);
136139
let hover_code = test_utils::get_hover_markdown(&mut session, &file_symbol, &file_info, 31, 48).unwrap_or_default();
@@ -164,6 +167,10 @@ fn test_definition() {
164167
assert!(PathBuf::from(&module2_test_file).exists(), "Test file does not exist: {}", module1_test_file);
165168
let mut session = setup::setup::create_session(&mut odoo);
166169

170+
// Use Lazy value for partner and Country class names
171+
let partner_class_name = test_utils::PARTNER_CLASS_NAME(session.sync_odoo.full_version.as_str());
172+
let country_class_name = test_utils::COUNTRY_CLASS_NAME(session.sync_odoo.full_version.as_str());
173+
167174
// Get file symbol and file info
168175
let file_mgr = session.sync_odoo.get_file_mgr();
169176
let m1_tf_file_info = file_mgr.borrow().get_file_info(&module1_test_file).unwrap();
@@ -219,7 +226,7 @@ fn test_definition() {
219226
assert_eq!(file_mgr.borrow().text_range_to_range(&mut session, &module1_test_file, sym_partner_id[0].borrow().range()), partner_id_locs[0].range, "Expected partner_id to be at the same location as the field");
220227

221228
let country_id_locs = test_utils::get_definition_locs(&mut session, &m1_tf_file_symbol, &m1_tf_file_info, 10, 74);
222-
let country_id_field_sym = session.sync_odoo.get_symbol(odoo_path, &(vec![Sy!("odoo"), Sy!("addons"), Sy!("base"), Sy!("models"), Sy!("res_partner")], vec![Sy!("Partner"), Sy!("country_id")]), u32::MAX);
229+
let country_id_field_sym = session.sync_odoo.get_symbol(odoo_path, &(vec![Sy!("odoo"), Sy!("addons"), Sy!("base"), Sy!("models"), Sy!("res_partner")], vec![Sy!(partner_class_name), Sy!("country_id")]), u32::MAX);
223230
assert_eq!(country_id_field_sym.len(), 1, "Expected 1 location for country_id");
224231
let country_id_field_sym = country_id_field_sym[0].clone();
225232
let country_id_file = country_id_field_sym.borrow().get_file().unwrap().upgrade().unwrap().borrow().paths()[0].clone();
@@ -229,7 +236,7 @@ fn test_definition() {
229236

230237
// now the same for phone_code
231238
let phone_code_locs = test_utils::get_definition_locs(&mut session, &m1_tf_file_symbol, &m1_tf_file_info, 10, 86);
232-
let phone_code_field_sym = session.sync_odoo.get_symbol(odoo_path, &(vec![Sy!("odoo"), Sy!("addons"), Sy!("base"), Sy!("models"), Sy!("res_country")], vec![Sy!("Country"), Sy!("phone_code")]), u32::MAX);
239+
let phone_code_field_sym = session.sync_odoo.get_symbol(odoo_path, &(vec![Sy!("odoo"), Sy!("addons"), Sy!("base"), Sy!("models"), Sy!("res_country")], vec![Sy!(country_class_name), Sy!("phone_code")]), u32::MAX);
233240
assert_eq!(phone_code_field_sym.len(), 1, "Expected 1 location for phone_code");
234241
let phone_code_field_sym = phone_code_field_sym[0].clone();
235242
let phone_code_file = phone_code_field_sym.borrow().get_file().unwrap().upgrade().unwrap().borrow().paths()[0].clone();

server/tests/test_utils.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,31 @@
1-
use std::{cell::RefCell, rc::Rc};
1+
use once_cell::sync::Lazy;
2+
use std::{cell::RefCell, rc::Rc, cmp::Ordering};
23

3-
use odoo_ls_server::{core::{file_mgr::FileInfo, symbols::symbol::Symbol}, threads::SessionInfo};
4+
use odoo_ls_server::{core::{file_mgr::FileInfo, symbols::symbol::Symbol}, threads::SessionInfo, utils::compare_semver};
45

56

7+
/// Returns the correct class name for Partner/ResPartner depending on Odoo version
8+
pub static PARTNER_CLASS_NAME: Lazy<fn(&str) -> &'static str> = Lazy::new(|| {
9+
|full_version: &str| {
10+
if compare_semver(full_version, "18.1") >= Ordering::Equal {
11+
"ResPartner"
12+
} else {
13+
"Partner"
14+
}
15+
}
16+
});
17+
18+
/// Returns the correct class name for Country/ResCountry depending on Odoo version
19+
pub static COUNTRY_CLASS_NAME: Lazy<fn(&str) -> &'static str> = Lazy::new(|| {
20+
|full_version: &str| {
21+
if compare_semver(full_version, "18.1") >= Ordering::Equal {
22+
"ResCountry"
23+
} else {
24+
"Country"
25+
}
26+
}
27+
});
28+
629

730
/// Helper to get hover markdown string at a given (line, character)
831
pub fn get_hover_markdown(session: &mut SessionInfo, file_symbol: &Rc<RefCell<Symbol>>, file_info: &Rc<RefCell<FileInfo>>, line: u32, character: u32) -> Option<String> {

0 commit comments

Comments
 (0)