Skip to content

Commit 606c12e

Browse files
mmahroussfda-odoo
authored andcommitted
[IMP] server: move decorator handlers to hooks
1 parent 45e8866 commit 606c12e

File tree

2 files changed

+200
-159
lines changed

2 files changed

+200
-159
lines changed

server/src/core/python_arch_eval.rs

Lines changed: 9 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,11 @@ impl PythonArchEval {
9898
};
9999
self.visit_sub_stmts(session, &ast);
100100
if !self.file_mode {
101-
self.handle_func_decorators(session, maybe_func_stmt, self.sym_stack[0].clone());
102-
PythonArchEval::handle_function_returns(session, maybe_func_stmt, &self.sym_stack[0], &ast.last().unwrap().range().end(), &mut self.diagnostics);
101+
let func_stmt = maybe_func_stmt.unwrap();
102+
self.diagnostics.extend(
103+
PythonArchEvalHooks::handle_func_decorators(session, func_stmt, self.sym_stack[0].clone(), self.file.clone(), self.current_step)
104+
);
105+
PythonArchEval::handle_function_returns(session, func_stmt, &self.sym_stack[0], &ast.last().unwrap().range().end(), &mut self.diagnostics);
103106
PythonArchEval::handle_func_evaluations(ast, &self.sym_stack[0]);
104107
}
105108
session.current_noqa = old_noqa;
@@ -766,7 +769,7 @@ impl PythonArchEval {
766769
self.visit_sub_stmts(session, &func_stmt.body);
767770
self.sym_stack.pop();
768771
session.current_noqa = old_noqa;
769-
PythonArchEval::handle_function_returns(session, Some(func_stmt), &function_sym, &func_stmt.range.end(), &mut self.diagnostics);
772+
PythonArchEval::handle_function_returns(session, func_stmt, &function_sym, &func_stmt.range.end(), &mut self.diagnostics);
770773
PythonArchEval::handle_func_evaluations(&func_stmt.body, &function_sym);
771774
function_sym.borrow_mut().as_func_mut().arch_eval_status = BuildStatus::DONE;
772775
}
@@ -934,12 +937,12 @@ impl PythonArchEval {
934937
// Evaluate return annotation and add it to function evaluations
935938
fn handle_function_returns(
936939
session: &mut SessionInfo,
937-
func_stmt: Option<&StmtFunctionDef>,
940+
func_stmt: &StmtFunctionDef,
938941
func_sym: &Rc<RefCell<Symbol>>,
939942
max_infer: &TextSize,
940943
diagnostics: &mut Vec<Diagnostic>,
941944
) {
942-
if let Some(returns_ann) = func_stmt.and_then(|func_stmt| func_stmt.returns.as_ref()) {
945+
if let Some(returns_ann) = func_stmt.returns.as_ref() {
943946
let file_sym = func_sym.borrow().get_file().and_then(|file_weak| file_weak.upgrade());
944947
let mut deps = vec![vec![], vec![]];
945948
let (evaluations, diags) = Evaluation::eval_from_ast(
@@ -971,7 +974,7 @@ impl PythonArchEval {
971974

972975
// Handle function evaluation if traversing the body did not get any evaluations
973976
// First we check if it is a function signature with no body ( like in stubs ) like def func():...
974-
// If so we give it an Any evaluation because it is undetermined, otherwise we give it None, becauset that means
977+
// If so we give it an Any evaluation because it is undetermined, otherwise we give it None, because that means
975978
// we have a body but no return statement, which defaults to return None at the end
976979
fn handle_func_evaluations(
977980
func_body: &Vec<Stmt>,
@@ -992,72 +995,6 @@ impl PythonArchEval {
992995
}
993996
}
994997

995-
fn handle_api_returns_decorator(&mut self, session: &mut SessionInfo, func_sym: Rc<RefCell<Symbol>>, arguments: &Arguments){
996-
let Some(Expr::StringLiteral(expr)) = arguments.args.first() else {return};
997-
let returns_str = expr.value.to_string();
998-
if returns_str == S!("self"){
999-
func_sym.borrow_mut().set_evaluations(vec![Evaluation::new_self()]);
1000-
return;
1001-
}
1002-
let Some(model) = session.sync_odoo.models.get(&oyarn!("{}", returns_str)).cloned() else {
1003-
add_diagnostic(&mut self.diagnostics, Diagnostic::new(
1004-
FileMgr::textRange_to_temporary_Range(&expr.range()),
1005-
Some(DiagnosticSeverity::ERROR),
1006-
Some(NumberOrString::String(S!("OLS30102"))),
1007-
Some(EXTENSION_NAME.to_string()),
1008-
S!("Unknown model. Check your addons path"),
1009-
None,
1010-
None,
1011-
), &session.current_noqa);
1012-
return;
1013-
};
1014-
let Some(ref main_model_sym) = model.borrow().get_main_symbols(session, func_sym.borrow().find_module()).first().cloned() else {
1015-
add_diagnostic(&mut self.diagnostics, Diagnostic::new(
1016-
FileMgr::textRange_to_temporary_Range(&expr.range()),
1017-
Some(DiagnosticSeverity::ERROR),
1018-
Some(NumberOrString::String(S!("OLS30101"))),
1019-
Some(EXTENSION_NAME.to_string()),
1020-
S!("This model is not in the dependencies of your module."),
1021-
None,
1022-
None,
1023-
), &session.current_noqa);
1024-
return
1025-
};
1026-
func_sym.borrow_mut().set_evaluations(vec![Evaluation::eval_from_symbol(&Rc::downgrade(main_model_sym), Some(false))]);
1027-
}
1028-
1029-
/// For @api.constrains and @api.onchange, both can only take a simple field name
1030-
fn handle_api_simple_field_decorator(&mut self, session: &mut SessionInfo, func_sym: Rc<RefCell<Symbol>>, arguments: &Arguments){
1031-
let from_module = func_sym.borrow().find_module();
1032-
1033-
let Some(class_sym) = func_sym.borrow().get_in_parents(&vec![SymType::CLASS], true).and_then(
1034-
|class_sym_weak| class_sym_weak.upgrade()
1035-
) else {
1036-
return;
1037-
};
1038-
1039-
let Some(model_name) = class_sym.borrow().as_class_sym()._model.as_ref().map(|model| &model.name).cloned() else {
1040-
return;
1041-
};
1042-
1043-
for arg in arguments.args.iter() {
1044-
let Expr::StringLiteral(expr) = arg else {return};
1045-
let field_name = expr.value.to_string();
1046-
let (syms, _) = class_sym.borrow().get_member_symbol(session, &field_name, from_module.clone(), false, false, true, false);
1047-
if syms.is_empty(){
1048-
add_diagnostic(&mut self.diagnostics, Diagnostic::new(
1049-
FileMgr::textRange_to_temporary_Range(&expr.range()),
1050-
Some(DiagnosticSeverity::ERROR),
1051-
Some(NumberOrString::String(S!("OLS30323"))),
1052-
Some(EXTENSION_NAME.to_string()),
1053-
format!("Field {field_name} does not exist on model {model_name}"),
1054-
None,
1055-
None,
1056-
), &session.current_noqa);
1057-
}
1058-
}
1059-
}
1060-
1061998
pub fn get_nested_sub_field(
1062999
session: &mut SessionInfo,
10631000
field_name: &String,
@@ -1098,82 +1035,4 @@ impl PythonArchEval {
10981035
}
10991036
syms
11001037
}
1101-
1102-
/// For @api.depends, which can take a nested simple field name
1103-
fn handle_api_nested_field_decorator(&mut self, session: &mut SessionInfo, func_sym: Rc<RefCell<Symbol>>, arguments: &Arguments){
1104-
let from_module = func_sym.borrow().find_module();
1105-
1106-
let Some(class_sym) = func_sym.borrow().get_in_parents(&vec![SymType::CLASS], true).and_then(
1107-
|class_sym_weak| class_sym_weak.upgrade()
1108-
) else {
1109-
return;
1110-
};
1111-
1112-
let Some(model_name) = class_sym.borrow().as_class_sym()._model.as_ref().map(|model| &model.name).cloned() else {
1113-
return;
1114-
};
1115-
1116-
for arg in arguments.args.iter() {
1117-
let Expr::StringLiteral(expr) = arg else {return};
1118-
let field_name = expr.value.to_string();
1119-
let syms = PythonArchEval::get_nested_sub_field(session, &field_name, class_sym.clone(), from_module.clone());
1120-
if syms.is_empty(){
1121-
add_diagnostic(&mut self.diagnostics, Diagnostic::new(
1122-
FileMgr::textRange_to_temporary_Range(&expr.range()),
1123-
Some(DiagnosticSeverity::ERROR),
1124-
Some(NumberOrString::String(S!("OLS30323"))),
1125-
Some(EXTENSION_NAME.to_string()),
1126-
format!("Field {field_name} does not exist on model {model_name}"),
1127-
None,
1128-
None,
1129-
), &session.current_noqa);
1130-
}
1131-
}
1132-
}
1133-
1134-
/// Read function decorators and set evaluations where applicable
1135-
/// - api.returns -> self -> Self, string -> model name if exists + validate
1136-
/// - validates api.depends/onchange/constrains
1137-
fn handle_func_decorators(
1138-
&mut self,
1139-
session: &mut SessionInfo,
1140-
maybe_func_stmt: Option<&StmtFunctionDef>,
1141-
func_sym: Rc<RefCell<Symbol>>,
1142-
){
1143-
let Some(func_stmt) = maybe_func_stmt else {return};
1144-
for decorator in func_stmt.decorator_list.iter(){
1145-
let (decorator_base, decorator_args) = match &decorator.expression {
1146-
Expr::Call(call_expr) => {
1147-
(&call_expr.func, &call_expr.arguments)
1148-
},
1149-
_ => {continue;}
1150-
};
1151-
if decorator_args.args.is_empty(){
1152-
continue; // All the decorators we handle have at least one arg for now
1153-
}
1154-
let mut deps = vec![vec![], vec![]];
1155-
if !self.file_mode {
1156-
deps.push(vec![]);
1157-
}
1158-
let (dec_evals, diags) = Evaluation::eval_from_ast(session, &decorator_base, self.sym_stack.last().unwrap().clone(), &func_stmt.range.start(), &mut deps);
1159-
Symbol::insert_dependencies(&self.file, &mut deps, self.current_step);
1160-
self.diagnostics.extend(diags);
1161-
for decorator_eval in dec_evals.iter(){
1162-
let EvaluationSymbolPtr::WEAK(decorator_eval_sym_weak) = decorator_eval.symbol.get_symbol(session, &mut None, &mut self.diagnostics, None) else {continue};
1163-
let Some(dec_sym) = decorator_eval_sym_weak.weak.upgrade() else {continue};
1164-
let dec_sym_tree = dec_sym.borrow().get_tree();
1165-
if !dec_sym_tree.0.ends_with(&[Sy!("odoo"), Sy!("api")]){
1166-
continue;
1167-
}
1168-
if dec_sym_tree.1 == vec![Sy!("returns")] && SyncOdoo::is_in_main_entry(session, &dec_sym_tree.0){
1169-
self.handle_api_returns_decorator(session, func_sym.clone(), decorator_args);
1170-
} else if [vec![Sy!("onchange")], vec![Sy!("constrains")]].contains(&dec_sym_tree.1) && SyncOdoo::is_in_main_entry(session, &dec_sym_tree.0) {
1171-
self.handle_api_simple_field_decorator(session, func_sym.clone(), decorator_args);
1172-
} else if dec_sym_tree.1 == vec![Sy!("depends")] && SyncOdoo::is_in_main_entry(session, &dec_sym_tree.0) {
1173-
self.handle_api_nested_field_decorator(session, func_sym.clone(), decorator_args);
1174-
}
1175-
}
1176-
}
1177-
}
1178-
11791038
}

0 commit comments

Comments
 (0)