Skip to content

Commit 80b805c

Browse files
committed
[IMP] support subscript on object with multiple evaluations
This enables getting an evaluation for `request.env["some.model"]`, even though `env` has two possible evaluations (Environment and None).
1 parent 74a91a3 commit 80b805c

File tree

2 files changed

+90
-58
lines changed

2 files changed

+90
-58
lines changed

server/src/core/evaluation.rs

Lines changed: 59 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,7 +1149,7 @@ impl Evaluation {
11491149
evals.push(Evaluation::new_unbound(name));
11501150
}
11511151
},
1152-
ExprOrIdent::Expr(Expr::Subscript(sub)) => 'subscript_block: {
1152+
ExprOrIdent::Expr(Expr::Subscript(sub)) => {
11531153
let (eval_left, diags) = Evaluation::eval_from_ast(session, &sub.value, parent.clone(), max_infer, false, required_dependencies);
11541154
diagnostics.extend(diags);
11551155
// TODO handle multiple eval_left
@@ -1161,73 +1161,74 @@ impl Evaluation {
11611161
return AnalyzeAstResult::from_only_diagnostics(diagnostics);
11621162
}
11631163
let bases = Symbol::follow_ref(&base, session, &mut None, false, false, None);
1164-
if bases.len() != 1 {
1164+
if bases.is_empty() {
11651165
return AnalyzeAstResult::from_only_diagnostics(diagnostics);
11661166
}
1167-
let base = &bases[0];
1168-
match base {
1169-
EvaluationSymbolPtr::WEAK(base_sym_weak_eval) if base_sym_weak_eval.instance == Some(false) => {
1170-
if let Some(SymType::CLASS) = base.upgrade_weak().map(|s| s.borrow().typ()) {
1171-
// This is a Generic type (Field[int], or List[int]), for now we just return the main type/Class (Field/List)
1172-
// TODO: handle generic types
1173-
let mut new_base = base.clone();
1174-
if for_annotation {
1175-
new_base.as_mut_weak().instance = Some(true);
1167+
for base in &bases {
1168+
match base {
1169+
EvaluationSymbolPtr::WEAK(base_sym_weak_eval) if base_sym_weak_eval.instance == Some(false) => {
1170+
if let Some(SymType::CLASS) = base.upgrade_weak().map(|s| s.borrow().typ()) {
1171+
// This is a Generic type (Field[int], or List[int]), for now we just return the main type/Class (Field/List)
1172+
// TODO: handle generic types
1173+
let mut new_base = base.clone();
1174+
if for_annotation {
1175+
new_base.as_mut_weak().instance = Some(true);
1176+
}
1177+
evals.push(Evaluation {
1178+
symbol: EvaluationSymbol {
1179+
sym: new_base,
1180+
get_symbol_hook: None,
1181+
},
1182+
value: None,
1183+
range: Some(sub.range())
1184+
});
1185+
continue;
11761186
}
1177-
evals.push(Evaluation {
1178-
symbol: EvaluationSymbol {
1179-
sym: new_base,
1180-
get_symbol_hook: None,
1181-
},
1182-
value: None,
1183-
range: Some(sub.range())
1184-
});
1185-
break 'subscript_block;
11861187
}
1188+
_ => {}
11871189
}
1188-
_ => {}
1189-
}
1190-
let value = Evaluation::expr_to_str(session, &sub.slice, parent.clone(), max_infer, false, &mut diagnostics);
1191-
diagnostics.extend(value.1);
1192-
if let Some(value) = value.0 {
1193-
if !base.is_weak() {
1194-
return AnalyzeAstResult::from_only_diagnostics(diagnostics);
1195-
}
1196-
let parent_file_or_func = parent.clone().borrow().parent_file_or_function().as_ref().unwrap().upgrade().unwrap();
1197-
let is_in_validation = match parent_file_or_func.borrow().typ().clone() {
1198-
SymType::FILE | SymType::PACKAGE(_) | SymType::FUNCTION => {
1199-
parent_file_or_func.borrow().build_status(BuildSteps::VALIDATION) == BuildStatus::IN_PROGRESS
1200-
},
1201-
_ => {false}
1202-
};
1203-
let base = base.upgrade_weak().unwrap();
1204-
let get_item = base.borrow().get_content_symbol("__getitem__", u32::MAX).symbols;
1205-
if get_item.len() == 1 {
1206-
let get_item = &get_item[0];
1207-
let get_item = get_item.borrow();
1208-
if get_item.evaluations().is_some() && get_item.evaluations().unwrap().len() == 1 {
1209-
let get_item_eval = &get_item.evaluations().unwrap()[0];
1210-
if let Some(hook) = get_item_eval.symbol.get_symbol_hook {
1211-
context.as_mut().unwrap().insert(S!("args"), ContextValue::STRING(value));
1212-
let old_range = context.as_mut().unwrap().remove(&S!("range"));
1213-
context.as_mut().unwrap().insert(S!("range"), ContextValue::RANGE(sub.slice.range()));
1214-
context.as_mut().unwrap().insert(S!("is_in_validation"), ContextValue::BOOLEAN(is_in_validation));
1215-
let hook_result = hook(session, &get_item_eval.symbol, context, &mut diagnostics, Some(parent.clone()));
1216-
if let Some(hook_result) = hook_result {
1217-
match hook_result {
1218-
EvaluationSymbolPtr::WEAK(ref weak) => {
1219-
if !weak.weak.is_expired() {
1190+
let value = Evaluation::expr_to_str(session, &sub.slice, parent.clone(), max_infer, false, &mut diagnostics);
1191+
diagnostics.extend(value.1);
1192+
if let Some(value) = value.0 {
1193+
if !base.is_weak() {
1194+
continue;
1195+
}
1196+
let parent_file_or_func = parent.clone().borrow().parent_file_or_function().as_ref().unwrap().upgrade().unwrap();
1197+
let is_in_validation = match parent_file_or_func.borrow().typ().clone() {
1198+
SymType::FILE | SymType::PACKAGE(_) | SymType::FUNCTION => {
1199+
parent_file_or_func.borrow().build_status(BuildSteps::VALIDATION) == BuildStatus::IN_PROGRESS
1200+
},
1201+
_ => {false}
1202+
};
1203+
let base = base.upgrade_weak().unwrap();
1204+
let get_item = base.borrow().get_content_symbol("__getitem__", u32::MAX).symbols;
1205+
if get_item.len() == 1 {
1206+
let get_item = &get_item[0];
1207+
let get_item = get_item.borrow();
1208+
if get_item.evaluations().is_some() && get_item.evaluations().unwrap().len() == 1 {
1209+
let get_item_eval = &get_item.evaluations().unwrap()[0];
1210+
if let Some(hook) = get_item_eval.symbol.get_symbol_hook {
1211+
context.as_mut().unwrap().insert(S!("args"), ContextValue::STRING(value));
1212+
let old_range = context.as_mut().unwrap().remove(&S!("range"));
1213+
context.as_mut().unwrap().insert(S!("range"), ContextValue::RANGE(sub.slice.range()));
1214+
context.as_mut().unwrap().insert(S!("is_in_validation"), ContextValue::BOOLEAN(is_in_validation));
1215+
let hook_result = hook(session, &get_item_eval.symbol, context, &mut diagnostics, Some(parent.clone()));
1216+
if let Some(hook_result) = hook_result {
1217+
match hook_result {
1218+
EvaluationSymbolPtr::WEAK(ref weak) => {
1219+
if !weak.weak.is_expired() {
1220+
evals.push(Evaluation::eval_from_ptr(&hook_result));
1221+
}
1222+
},
1223+
_ => {
12201224
evals.push(Evaluation::eval_from_ptr(&hook_result));
12211225
}
1222-
},
1223-
_ => {
1224-
evals.push(Evaluation::eval_from_ptr(&hook_result));
12251226
}
12261227
}
1228+
context.as_mut().unwrap().remove(&S!("args"));
1229+
context.as_mut().unwrap().remove(&S!("is_in_validation"));
1230+
context.as_mut().unwrap().insert(S!("range"), old_range.unwrap());
12271231
}
1228-
context.as_mut().unwrap().remove(&S!("args"));
1229-
context.as_mut().unwrap().remove(&S!("is_in_validation"));
1230-
context.as_mut().unwrap().insert(S!("range"), old_range.unwrap());
12311232
}
12321233
}
12331234
}

server/tests/test_controller.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ fn test_controller() {
3737

3838
test_request_type_hover(&mut session, &file_symbol, &file_info);
3939
test_request_env_definition(&mut session, &file_symbol, &file_info);
40+
test_request_env_subscript(&mut session, &file_symbol, &file_info);
4041
}
4142

4243
/// Test that hovering over 'request' shows Request class
@@ -118,4 +119,34 @@ fn test_request_env_definition(
118119
);
119120
}
120121

122+
/// Test that request.env["model_name"] resolves to Model instance
123+
fn test_request_env_subscript(
124+
session: &mut SessionInfo,
125+
file_symbol: &Rc<RefCell<Symbol>>,
126+
file_info: &Rc<RefCell<FileInfo>>
127+
) {
128+
let partner_class = test_utils::PARTNER_CLASS_NAME(&session.sync_odoo.full_version);
129+
130+
// Test 1: Hover over 'partner' variable (line 19: partner = request.env['res.partner'])
131+
// Should show Partner/ResPartner class
132+
let hover_text = test_utils::get_hover_markdown(session, file_symbol, file_info, 18, 12)
133+
.expect("Should get hover text for 'partner' variable");
134+
135+
assert!(
136+
hover_text.contains(partner_class),
137+
"Hover over 'partner' should show {} class. Got: {}",
138+
partner_class,
139+
hover_text
140+
);
121141

142+
// Test 2: Hover over .search on request.env['res.partner'].search (line 29)
143+
// Should display markdown content for the search method
144+
let hover_text = test_utils::get_hover_markdown(session, file_symbol, file_info, 27, 46)
145+
.expect("Should get hover text for .search method");
146+
147+
assert!(
148+
hover_text.contains("def search"),
149+
"Hover over .search should have markdown content and mention 'search'. Got: {}",
150+
hover_text
151+
);
152+
}

0 commit comments

Comments
 (0)