diff --git a/server/src/core/evaluation.rs b/server/src/core/evaluation.rs index 062f2ebe..49a07938 100644 --- a/server/src/core/evaluation.rs +++ b/server/src/core/evaluation.rs @@ -198,7 +198,19 @@ pub type Context = HashMap; * diagnostics: a vec the hook can fill to add diagnostics * file_symbol: if provided, can be used to add dependencies */ -type GetSymbolHook = fn (session: &mut SessionInfo, eval: &EvaluationSymbol, context: &mut Option, diagnostics: &mut Vec, scope: Option>>) -> Option; +type GetSymbolHookCallable = fn (session: &mut SessionInfo, eval: &EvaluationSymbol, context: &mut Option, diagnostics: &mut Vec, scope: Option>>) -> Option; + +#[derive(Debug, Clone)] +pub struct GetSymbolHook { + pub callable: GetSymbolHookCallable, + pub name: String +} + +impl PartialEq for GetSymbolHook { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} #[derive(Debug, Clone)] @@ -440,7 +452,7 @@ impl Evaluation { EvaluationSymbolPtr::WEAK(_) => { //take the weak by get_symbol instead of the match let symbol_eval = self.symbol.get_symbol(session, &mut None, &mut vec![], Some(function.clone())); - let out_of_scope = Symbol::follow_ref(&symbol_eval, session, &mut None, false, false, Some(function.clone())); + let out_of_scope = Symbol::follow_ref(&symbol_eval, session, &mut None, false, false, None, Some(function.clone())); for sym in out_of_scope { if !sym.is_expired_if_weak() { res.push(Evaluation { @@ -469,7 +481,7 @@ impl Evaluation { if eval_symbol.is_expired_if_weak() { return None; } - let evals = Symbol::follow_ref(&eval_symbol, session, context, false, true, None); + let evals = Symbol::follow_ref(&eval_symbol, session, context, false, true, None, None); if evals.len() == 1 { let eval = &evals[0]; match eval { @@ -773,7 +785,7 @@ impl Evaluation { } let base_eval_ptrs: Vec = base_evals.iter().map(|base_eval| { let base_sym_weak_eval_base = base_eval.symbol.get_symbol_weak_transformed(session, context, &mut diagnostics, None); - Symbol::follow_ref(&base_sym_weak_eval_base, session, context, true, false, None) + Symbol::follow_ref(&base_sym_weak_eval_base, session, context, true, false, None, None) }).flatten().collect(); let parent_file_or_func = parent.clone().borrow().parent_file_or_function().as_ref().unwrap().upgrade().unwrap(); @@ -805,7 +817,7 @@ impl Evaluation { let res = class_sym_weak_eval.weak.upgrade().and_then(|class_sym|{ let class_sym_weak_eval = &Symbol::follow_ref(&&EvaluationSymbolPtr::WEAK(EvaluationSymbolWeak::new( Rc::downgrade(&class_sym), None, false - )), session, &mut None, false, false, None)[0]; + )), session, &mut None, false, false, None, None)[0]; if class_sym_weak_eval.upgrade_weak().unwrap().borrow().typ() != SymType::CLASS{ return None; } @@ -834,7 +846,7 @@ impl Evaluation { let object_or_type_weak_eval = &Symbol::follow_ref( &object_or_type_eval[0].symbol.get_symbol( session, context, &mut diagnostics, Some(parent.clone())), - session, &mut None, false, false, None)[0]; + session, &mut None, false, false, None, None)[0]; if object_or_type_weak_eval.is_weak() { is_instance = Some(object_or_type_weak_eval.as_weak().instance.unwrap_or(default_instance)); } else { @@ -1038,7 +1050,7 @@ impl Evaluation { if base_ref.is_expired_if_weak() { return AnalyzeAstResult::from_only_diagnostics(diagnostics); } - let bases = Symbol::follow_ref(&base_ref, session, context, false, false, None); + let bases = Symbol::follow_ref(&base_ref, session, context, false, false, None, None); for ibase in bases.iter() { let base_loc = ibase.upgrade_weak(); if let Some(base_loc) = base_loc { @@ -1149,7 +1161,7 @@ impl Evaluation { evals.push(Evaluation::new_unbound(name)); } }, - ExprOrIdent::Expr(Expr::Subscript(sub)) => 'subscript_block: { + ExprOrIdent::Expr(Expr::Subscript(sub)) => { let (eval_left, diags) = Evaluation::eval_from_ast(session, &sub.value, parent.clone(), max_infer, false, required_dependencies); diagnostics.extend(diags); // TODO handle multiple eval_left @@ -1160,74 +1172,69 @@ impl Evaluation { if base.is_expired_if_weak() { return AnalyzeAstResult::from_only_diagnostics(diagnostics); } - let bases = Symbol::follow_ref(&base, session, &mut None, false, false, None); - if bases.len() != 1 { - return AnalyzeAstResult::from_only_diagnostics(diagnostics); - } - let base = &bases[0]; - match base { - EvaluationSymbolPtr::WEAK(base_sym_weak_eval) if base_sym_weak_eval.instance == Some(false) => { - if let Some(SymType::CLASS) = base.upgrade_weak().map(|s| s.borrow().typ()) { - // This is a Generic type (Field[int], or List[int]), for now we just return the main type/Class (Field/List) - // TODO: handle generic types - let mut new_base = base.clone(); - if for_annotation { - new_base.as_mut_weak().instance = Some(true); - } - evals.push(Evaluation { - symbol: EvaluationSymbol { - sym: new_base, - get_symbol_hook: None, - }, - value: None, - range: Some(sub.range()) - }); - break 'subscript_block; - } - } - _ => {} - } + let bases = Symbol::follow_ref(&base, session, &mut None, false, false, None, None); let value = Evaluation::expr_to_str(session, &sub.slice, parent.clone(), max_infer, false, &mut diagnostics); diagnostics.extend(value.1); - if let Some(value) = value.0 { - if !base.is_weak() { - return AnalyzeAstResult::from_only_diagnostics(diagnostics); + for base in bases.iter() { + match base { + EvaluationSymbolPtr::WEAK(base_sym_weak_eval) if base_sym_weak_eval.instance == Some(false) => { + if let Some(SymType::CLASS) = base.upgrade_weak().map(|s| s.borrow().typ()) { + // This is a Generic type (Field[int], or List[int]), for now we just return the main type/Class (Field/List) + // TODO: handle generic types + let mut new_base = base.clone(); + if for_annotation { + new_base.as_mut_weak().instance = Some(true); + } + evals.push(Evaluation { + symbol: EvaluationSymbol { + sym: new_base, + get_symbol_hook: None, + }, + value: None, + range: Some(sub.range()) + }); + continue; + } + } + _ => {} } - let parent_file_or_func = parent.clone().borrow().parent_file_or_function().as_ref().unwrap().upgrade().unwrap(); - let is_in_validation = match parent_file_or_func.borrow().typ().clone() { - SymType::FILE | SymType::PACKAGE(_) | SymType::FUNCTION => { - parent_file_or_func.borrow().build_status(BuildSteps::VALIDATION) == BuildStatus::IN_PROGRESS - }, - _ => {false} - }; - let base = base.upgrade_weak().unwrap(); - let get_item = base.borrow().get_content_symbol("__getitem__", u32::MAX).symbols; - if get_item.len() == 1 { - let get_item = &get_item[0]; - let get_item = get_item.borrow(); - if get_item.evaluations().is_some() && get_item.evaluations().unwrap().len() == 1 { - let get_item_eval = &get_item.evaluations().unwrap()[0]; - if let Some(hook) = get_item_eval.symbol.get_symbol_hook { - context.as_mut().unwrap().insert(S!("args"), ContextValue::STRING(value)); - let old_range = context.as_mut().unwrap().remove(&S!("range")); - context.as_mut().unwrap().insert(S!("range"), ContextValue::RANGE(sub.slice.range())); - context.as_mut().unwrap().insert(S!("is_in_validation"), ContextValue::BOOLEAN(is_in_validation)); - let hook_result = hook(session, &get_item_eval.symbol, context, &mut diagnostics, Some(parent.clone())); - if let Some(hook_result) = hook_result { - match hook_result { - EvaluationSymbolPtr::WEAK(ref weak) => { - if !weak.weak.is_expired() { + if base.is_weak() && let Some(value) = &value.0 { + let base = base.upgrade_weak().unwrap(); + let get_item = base.borrow().get_content_symbol("__getitem__", u32::MAX).symbols; + if get_item.len() == 1 { + let get_item = &get_item[0]; + let get_item = get_item.borrow(); + if get_item.evaluations().is_some() && get_item.evaluations().unwrap().len() == 1 { + let get_item_eval = &get_item.evaluations().unwrap()[0]; + if let Some(hook) = get_item_eval.symbol.get_symbol_hook.as_ref() { + let parent_file_or_func = parent.clone().borrow().parent_file_or_function().as_ref().unwrap().upgrade().unwrap(); + let is_in_validation = match parent_file_or_func.borrow().typ().clone() { + SymType::FILE | SymType::PACKAGE(_) | SymType::FUNCTION => { + parent_file_or_func.borrow().build_status(BuildSteps::VALIDATION) == BuildStatus::IN_PROGRESS + }, + _ => {false} + }; + context.as_mut().unwrap().insert(S!("args"), ContextValue::STRING(value.clone())); + let old_range = context.as_mut().unwrap().remove(&S!("range")); + context.as_mut().unwrap().insert(S!("range"), ContextValue::RANGE(sub.slice.range())); + context.as_mut().unwrap().insert(S!("is_in_validation"), ContextValue::BOOLEAN(is_in_validation)); + let hook_result = (hook.callable)(session, &get_item_eval.symbol, context, &mut diagnostics, Some(parent.clone())); + if let Some(hook_result) = hook_result { + match hook_result { + EvaluationSymbolPtr::WEAK(ref weak) => { + if !weak.weak.is_expired() { + evals.push(Evaluation::eval_from_ptr(&hook_result)); + } + }, + _ => { evals.push(Evaluation::eval_from_ptr(&hook_result)); } - }, - _ => { - evals.push(Evaluation::eval_from_ptr(&hook_result)); } } + context.as_mut().unwrap().remove(&S!("args")); + context.as_mut().unwrap().remove(&S!("is_in_validation")); + context.as_mut().unwrap().insert(S!("range"), old_range.unwrap()); } - context.as_mut().unwrap().remove(&S!("args")); - context.as_mut().unwrap().remove(&S!("is_in_validation")); - context.as_mut().unwrap().insert(S!("range"), old_range.unwrap()); } } } @@ -1277,7 +1284,7 @@ impl Evaluation { diagnostics.extend(diags); for base in bases.into_iter(){ let base_sym_weak_eval= base.symbol.get_symbol_weak_transformed(session, context, &mut diagnostics, None); - let base_eval_ptrs = Symbol::follow_ref(&base_sym_weak_eval, session, context, true, false, None); + let base_eval_ptrs = Symbol::follow_ref(&base_sym_weak_eval, session, context, true, false, None, None); 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}; @@ -1352,8 +1359,7 @@ impl Evaluation { }, ArgumentType::KWARG => { kwarg_index = index as i32; - }, - _ => {} + } } } if !function.is_static { @@ -1461,7 +1467,7 @@ impl Evaluation { diagnostics } - fn process_argument_diagnostics(session: &SessionInfo, expr_call: &ExprCall, diagnostics: Vec>, eval_count: usize) -> Vec { + fn process_argument_diagnostics(session: &SessionInfo, expr_call: &ExprCall, diagnostics: Vec>, _eval_count: usize) -> Vec { let mut filtered_diagnostics = vec![]; //iter through diagnostics and check that each evaluation has the same amount of diagnostics with code OLS01007 or OLS01008 or OLS01010 let all_same_issues = diagnostics.iter().fold_while(None, |acc, diags| { @@ -1794,8 +1800,8 @@ impl EvaluationSymbol { /* Execute Hook, then return the effective EvaluationSymbolPtr */ pub fn get_symbol(&self, session: &mut SessionInfo, context: &mut Option, diagnostics: &mut Vec, file_symbol: Option>>) -> EvaluationSymbolPtr { let mut custom_eval = None; - if let Some(hook) = self.get_symbol_hook { - custom_eval = hook(session, self, context, diagnostics, file_symbol); + if let Some(hook) = self.get_symbol_hook.as_ref() { + custom_eval = (hook.callable)(session, self, context, diagnostics, file_symbol); } custom_eval.as_ref().unwrap_or(&self.sym).clone() } @@ -1846,4 +1852,4 @@ impl EvaluationSymbolPtr { _ => panic!("Not an EvaluationSymbolWeak") } } -} \ No newline at end of file +} diff --git a/server/src/core/import_resolver.rs b/server/src/core/import_resolver.rs index 6aad4290..265455b9 100644 --- a/server/src/core/import_resolver.rs +++ b/server/src/core/import_resolver.rs @@ -22,21 +22,27 @@ pub struct ImportResult { pub name: OYarn, //the last imported element pub var_name: OYarn, // the effective symbol name (asname, or first part in a import A.B.C) pub found: bool, - pub symbol: Rc>, + pub symbols: Vec>>, pub file_tree: Vec, //contains only the first part of a Tree pub range: TextRange, } -//class used to cache import results in a build execution to speed up subsequent imports. +//class used to cache import results in a build execution to speed up subsequent imports. //It means of course than a modification during the build will not be taken into account, but it should be ok because reloaded after the build #[derive(Debug)] pub struct ImportCache { - pub modules: HashMap>>>, - pub main_modules: HashMap>>>, + pub modules: HashMap>>>>, + pub main_modules: HashMap>>>>, } -fn resolve_import_stmt_hook(alias: &Alias, from_symbol: &Option>>, session: &mut SessionInfo, source_file_symbol: &Rc>, from_stmt: Option<&Identifier>, level: u32, diagnostics: &mut Option<&mut Vec>) -> Option{ - if session.sync_odoo.version_major >= 17 && alias.name.as_str() == "Form" && (*(from_symbol.as_ref().unwrap())).borrow().get_main_entry_tree(session).0 == vec!["odoo", "tests", "common"]{ +fn resolve_import_stmt_hook(alias: &Alias, from_symbols: &Option>>>, session: &mut SessionInfo, source_file_symbol: &Rc>, from_stmt: Option<&Identifier>, level: u32, diagnostics: &mut Option<&mut Vec>) -> Option{ + if !(session.sync_odoo.version_major >= 17 && alias.name.as_str() == "Form"){ + return None; + } + for from_symbol in from_symbols.iter().flatten() { + if from_symbol.borrow().get_main_entry_tree(session).0 != vec!["odoo", "tests", "common"] { + continue; + } let mut results = resolve_import_stmt(session, source_file_symbol, Some(&Identifier::new(S!("odoo.tests"), from_stmt.unwrap().range)), &[alias.clone()], level, &mut None); if let Some(diagnostic) = diagnostics.as_mut() { if let Some(diagnostic_base) = create_diagnostic(&session, DiagnosticCode::OLS03301, &[]) { @@ -47,10 +53,9 @@ fn resolve_import_stmt_hook(alias: &Alias, from_symbol: &Option>, from_stmt: Option<&Identifier>, level: u32) -> (Option>>, Option>>, Vec) { +/// Resolve the base symbol from a from statement and level +pub fn resolve_from_stmt( + session: &mut SessionInfo, source_file_symbol: &Rc>, from_stmt: Option<&Identifier>, level: u32 +) -> (Option>>>, Option>>>, Vec) { let source_root = source_file_symbol.borrow().get_root().as_ref().unwrap().upgrade().unwrap(); let entry = source_root.borrow().get_entry().unwrap(); let _source_file_symbol_lock = source_file_symbol.borrow_mut(); - let file_tree = _resolve_packages( + let file_tree = resolve_packages( &_source_file_symbol_lock, level, from_stmt); @@ -85,37 +93,42 @@ pub fn resolve_from_stmt(session: &mut SessionInfo, source_file_symbol: &Rc>, from_stmt: Option<&Identifier>, name_aliases: &[Alias], level: u32, diagnostics: &mut Option<&mut Vec>) -> Vec { + for name_al in name_aliases.iter() { + if name_al.name.as_str() == "Self" { + // print!("Let's take a look");/\\ + } + } //A: search base of different imports let source_root = source_file_symbol.borrow().get_root().as_ref().unwrap().upgrade().unwrap(); let entry = source_root.borrow().get_entry().unwrap(); - let (from_symbol, fallback_sym, file_tree) = resolve_from_stmt(session, source_file_symbol, from_stmt, level); + let (from_symbols, fallback_syms, file_tree) = resolve_from_stmt(session, source_file_symbol, from_stmt, level); let mut result = vec![]; for alias in name_aliases { result.push(ImportResult{ name: OYarn::from(alias.name.as_ref().to_string()), var_name: OYarn::from(alias.asname.as_ref().unwrap_or(&alias.name).to_string()), found: false, - symbol: fallback_sym.as_ref().unwrap().clone(), + symbols: fallback_syms.as_ref().unwrap().clone(), file_tree: file_tree.clone(), range: alias.range.clone() }) } - if from_symbol.is_none() && from_stmt.is_some() { + if from_symbols.is_none() && from_stmt.is_some() { return result; } @@ -123,13 +136,13 @@ pub fn resolve_import_stmt(session: &mut SessionInfo, source_file_symbol: &Rc = name.split(".").map(|s| oyarn!("{}", s)).collect(); @@ -141,21 +154,21 @@ pub fn resolve_import_stmt(session: &mut SessionInfo, source_file_symbol: &Rc = vec![name_split.last().unwrap().clone()]; - let (mut next_symbol, mut fallback_sym) = _get_or_create_symbol( + let (mut next_symbol, mut fallback_sym) = get_or_create_symbol( session, &entry, source_path.as_str(), - from_symbol.clone(), + from_symbols.clone(), &name_first_part, None, 0); - if next_symbol.is_none() && name_split.len() == 1 && from_symbol.is_some() { + if next_symbol.is_none() && name_split.len() == 1 && from_symbols.is_some() { //check the last name is not a symbol in the file - let name_symbol_vec = from_symbol.as_ref().unwrap().borrow().get_symbol(&(vec![], name_first_part), u32::MAX); - next_symbol = name_symbol_vec.last().cloned(); + let name_symbol_vec = from_symbols.as_ref().unwrap().iter().flat_map(|s| s.borrow().get_symbol(&(vec![], name_first_part.clone()), u32::MAX)).collect::>(); + next_symbol = if name_symbol_vec.len() > 0 {Some(name_symbol_vec.clone())} else {None}; } if next_symbol.is_none() { - result[name_index as usize].symbol = fallback_sym.as_ref().unwrap_or(&source_root).clone(); + result[name_index as usize].symbols = fallback_sym.as_ref().unwrap_or(&vec![source_root.clone()]).clone(); continue; } if alias.asname.is_none() { @@ -165,10 +178,10 @@ pub fn resolve_import_stmt(session: &mut SessionInfo, source_file_symbol: &Rc 1 { // now we can search for the last symbol, or create it if it doesn't exist - let (mut last_symbol, fallback_sym) = _get_or_create_symbol( + let (mut last_symbol, fallback_sym) = get_or_create_symbol( session, &entry, "", @@ -195,11 +208,11 @@ pub fn resolve_import_stmt(session: &mut SessionInfo, source_file_symbol: &Rc>(); + last_symbol = if name_symbol_vec.len() > 0 {Some(name_symbol_vec.clone())} else {None}; if last_symbol.is_none() { if alias.asname.is_some() { - result[name_index as usize].symbol = fallback_sym.as_ref().unwrap_or(&source_root).clone(); + result[name_index as usize].symbols = fallback_sym.as_ref().unwrap_or(&vec![source_root.clone()]).clone(); } continue; } @@ -208,14 +221,14 @@ pub fn resolve_import_stmt(session: &mut SessionInfo, source_file_symbol: &Rc>, None } -fn _resolve_packages(from_file: &Symbol, level: u32, from_stmt: Option<&Identifier>) -> Vec { +fn resolve_packages(from_file: &Symbol, level: u32, from_stmt: Option<&Identifier>) -> Vec { let mut first_part_tree: Vec = vec![]; if level > 0 { let mut lvl = level; @@ -273,30 +286,40 @@ fn _resolve_packages(from_file: &Symbol, level: u32, from_stmt: Option<&Identifi first_part_tree } -fn _get_or_create_symbol(session: &mut SessionInfo, for_entry: &Rc>, from_path: &str, symbol: Option>>, names: &Vec, asname: Option, level: u32) -> (Option>>, Option>>) { - let mut sym: Option>> = symbol.clone(); - let mut last_symbol = symbol.clone(); +fn get_or_create_symbol( + session: &mut SessionInfo, for_entry: &Rc>, from_path: &str, symbol: Option>>>, names: &Vec, asname: Option, level: u32 +) -> (Option>>>, Option>>>) { + let mut syms = symbol.clone(); + let mut last_symbols = symbol.clone(); for branch in names.iter() { if branch.is_empty() { continue; } - match sym { - Some(ref s) => { - let mut next_symbol = s.borrow().get_symbol(&(vec![branch.clone()], vec![]), u32::MAX); - if next_symbol.is_empty() && matches!(s.borrow().typ(), SymType::ROOT | SymType::NAMESPACE | SymType::PACKAGE(_) | SymType::COMPILED | SymType::DISK_DIR) { - next_symbol = match _resolve_new_symbol(session, s.clone(), &branch, asname.clone()) { - Ok(v) => vec![v], - Err(_) => vec![] + if matches!(&syms, Some(s) if s.len() == 0) { + syms = None; + } + match syms { + Some(ref symbols) => { + let mut next_symbol = vec![]; + for s in symbols.iter() { + let mut current_batch_symbol = s.borrow().get_symbol(&(vec![branch.clone()], vec![]), u32::MAX); + if current_batch_symbol.is_empty() && matches!(s.borrow().typ(), SymType::ROOT | SymType::NAMESPACE | SymType::PACKAGE(_) | SymType::COMPILED | SymType::DISK_DIR) { + current_batch_symbol = match resolve_new_symbol(session, s.clone(), &branch, asname.clone()) { + Ok(v) => vec![v], + Err(_) => vec![] + } } + next_symbol.extend(current_batch_symbol.clone()); } if next_symbol.is_empty() { - sym = None; + syms = None; break; } - sym = Some(next_symbol[0].clone()); - last_symbol = Some(next_symbol[0].clone()); + syms = Some(next_symbol.clone()); + last_symbols = Some(next_symbol.clone()); }, None => { + // Can we have sym None and level != 0 ? maybe on get_all_valid_names? tbc if level == 0 { if let Some(ref cache) = session.sync_odoo.import_cache { let cache_module = if for_entry.borrow().typ == EntryPointType::MAIN || for_entry.borrow().typ == EntryPointType::ADDON { @@ -306,13 +329,13 @@ fn _get_or_create_symbol(session: &mut SessionInfo, for_entry: &Rc vec![v], Err(_) => vec![] } } - if next_symbol.is_empty() { + if next_symbols.is_empty() { continue; } if level == 0 { if entry.borrow().is_public() { if let Some(cache) = session.sync_odoo.import_cache.as_mut() { - cache.modules.insert(branch.clone(), Some(next_symbol[0].clone())); + cache.modules.insert(branch.clone(), Some(next_symbols.clone())); } } else if matches!(entry.borrow().typ, EntryPointType::MAIN | EntryPointType::ADDON) { if let Some(cache) = session.sync_odoo.import_cache.as_mut() { - cache.main_modules.insert(branch.clone(), Some(next_symbol[0].clone())); + cache.main_modules.insert(branch.clone(), Some(next_symbols.clone())); } } } found = true; - sym = Some(next_symbol[0].clone()); - last_symbol = Some(next_symbol[0].clone()); + syms = Some(next_symbols.clone()); + last_symbols = Some(next_symbols.clone()); break; } } @@ -365,18 +388,20 @@ fn _get_or_create_symbol(session: &mut SessionInfo, for_entry: &Rc>, name: &OYarn, asname: Option) -> Result>, String> { - if name == "" { +/// Resolve a new symbol from disk, creating it if found, or just creating a COMPILED symbol if a parent is COMPILED +/// parent : parent symbol where to search, either ROOT, NAMESPACE, PACKAGE, COMPILED or DISK_DIR +fn resolve_new_symbol(session: &mut SessionInfo, parent: Rc>, imported_name: &OYarn, asname: Option) -> Result>, String> { + if imported_name == "" { return Err("Empty name".to_string()); } if DEBUG_BORROW_GUARDS { @@ -385,17 +410,19 @@ fn _resolve_new_symbol(session: &mut SessionInfo, parent: Rc>, n } let sym_name: String = match asname { Some(asname_inner) => asname_inner.clone(), - None => name.to_string() + None => imported_name.to_string() }; + // COMPILED: we can only create a COMPILED symbol if (*parent).borrow().typ() == SymType::COMPILED { return Ok((*parent).borrow_mut().add_new_compiled(session, &sym_name, &S!(""))); } + // ROOT, NAMESPACE, PACKAGE or DISK_DIR: we can search on disk let paths = (*parent).borrow().paths().clone(); for path in paths.iter() { - let mut full_path = Path::new(path.as_str()).join(name.to_string()); + let mut full_path = Path::new(path.as_str()).join(imported_name.to_string()); for stub in session.sync_odoo.stubs_dirs.iter() { if path.as_str().to_string() == *stub { - full_path = full_path.join(name.to_string()); + full_path = full_path.join(imported_name.to_string()); } } if is_dir_cs(full_path.sanitize()) && (is_file_cs(full_path.join("__init__").with_extension("py").sanitize()) || @@ -456,7 +483,7 @@ fn _resolve_new_symbol(session: &mut SessionInfo, parent: Rc>, n /* Used for autocompletion. Given a base_name, return all valid names that can be used to complete it. -is_from indicates if the import is the X in "from X import Y". Else it is Y from "import Y" or "from X import Y" +is_from indicates if the completion item is the X in "from X import Y". Else it is Y from "import Y" or "from X import Y" */ pub fn get_all_valid_names(session: &mut SessionInfo, source_file_symbol: &Rc>, from_stmt: Option, import: String, level: u32, is_from: bool) -> HashMap { let (identifier_from, to_complete) = match from_stmt { @@ -503,7 +530,7 @@ pub fn get_all_valid_names(session: &mut SessionInfo, source_file_symbol: &Rc>(); if import_parts.len() > 1 { - let (next_symbol, _fallback_sym) = _get_or_create_symbol( - session, - &entry, - source_path.as_str(), - from_symbol.clone(), - &import_parts[0..import_parts.len()-1].iter().map(|s| oyarn!("{}", *s)).collect(), - None, - level, - ); + let (next_symbol, _fallback_sym) = get_or_create_symbol( + session, + &entry, + source_path.as_str(), + from_symbol.clone(), + &import_parts[0..import_parts.len()-1].iter().map(|s| oyarn!("{}", *s)).collect(), + None, + level, + ); if next_symbol.is_none() { return result; } @@ -530,7 +557,7 @@ pub fn get_all_valid_names(session: &mut SessionInfo, source_file_symbol: &Rc = vec![]; - if let Some(all) = import_result.symbol.borrow().get_content_symbol("__all__", u32::MAX).symbols.first().cloned() { - let all_value = Symbol::follow_ref(&EvaluationSymbolPtr::WEAK(EvaluationSymbolWeak::new( - Rc::downgrade(&all), None, false - )), session, &mut None, false, true, None); - if let Some(all_value_first) = all_value.get(0) { - if !all_value_first.is_expired_if_weak() { - let all_upgraded = all_value_first.upgrade_weak(); - if let Some(all_upgraded_unwrapped) = all_upgraded { - let all_upgraded_unwrapped_bw = (*all_upgraded_unwrapped).borrow(); - if all_upgraded_unwrapped_bw.evaluations().is_some() && all_upgraded_unwrapped_bw.evaluations().unwrap().len() == 1 { - let value = &all_upgraded_unwrapped_bw.evaluations().unwrap()[0].value; - if value.is_some() { - let (nf, parse_error) = self.extract_all_symbol_eval_values(&value.as_ref()); - if parse_error { - warn!("error during parsing __all__ import in file {}", (*import_result.symbol).borrow().paths()[0] ) + for import_symbol in import_result.symbols{ + if let Some(all) = import_symbol.borrow().get_content_symbol("__all__", u32::MAX).symbols.first().cloned() { + let all_value = Symbol::follow_ref(&EvaluationSymbolPtr::WEAK(EvaluationSymbolWeak::new( + Rc::downgrade(&all), None, false + )), session, &mut None, false, true, None, None); + if let Some(all_value_first) = all_value.get(0) { + if !all_value_first.is_expired_if_weak() { + let all_upgraded = all_value_first.upgrade_weak(); + if let Some(all_upgraded_unwrapped) = all_upgraded { + let all_upgraded_unwrapped_bw = (*all_upgraded_unwrapped).borrow(); + if all_upgraded_unwrapped_bw.evaluations().is_some() && all_upgraded_unwrapped_bw.evaluations().unwrap().len() == 1 { + let value = &all_upgraded_unwrapped_bw.evaluations().unwrap()[0].value; + if value.is_some() { + let (nf, parse_error) = self.extract_all_symbol_eval_values(&value.as_ref()); + if parse_error { + warn!("error during parsing __all__ import in file {}", import_symbol.borrow().paths()[0] ) + } + name_filter = nf; + all_name_allowed = false; + } else { + warn!("invalid __all__ import in file {} - no value found", import_symbol.borrow().paths()[0]) } - name_filter = nf; - all_name_allowed = false; } else { - warn!("invalid __all__ import in file {} - no value found", (*import_result.symbol).borrow().paths()[0]) + warn!("invalid __all__ import in file {} - multiple evaluation found", import_symbol.borrow().paths()[0]) } } else { - warn!("invalid __all__ import in file {} - multiple evaluation found", (*import_result.symbol).borrow().paths()[0]) + warn!("invalid __all__ import in file {} - localizedSymbol not found", import_symbol.borrow().paths()[0]) } } else { - warn!("invalid __all__ import in file {} - localizedSymbol not found", (*import_result.symbol).borrow().paths()[0]) + warn!("invalid __all__ import in file {} - expired symbol", import_symbol.borrow().paths()[0]) } } else { - warn!("invalid __all__ import in file {} - expired symbol", (*import_result.symbol).borrow().paths()[0]) + warn!("invalid __all__ import in file {} - no symbol found", import_symbol.borrow().paths()[0]) } - } else { - warn!("invalid __all__ import in file {} - no symbol found", (*import_result.symbol).borrow().paths()[0]) } - } - let mut dep_to_add = vec![]; - let sym_type = import_result.symbol.borrow().typ(); - if sym_type != SymType::COMPILED { - if !Rc::ptr_eq(self.sym_stack.last().unwrap(), &import_result.symbol) { /*We have to check that the imported symbol is not the current one. It can - happen for example in a .pyi that is importing the .pyd file with the same name. As both exists, odools will try to import the pyi a second time in the same file, - and so create a borrow error here - */ - let symbol = import_result.symbol.borrow(); - for (name, loc_syms) in symbol.iter_symbols() { - if all_name_allowed || name_filter.contains(&name) { - let variable = self.sym_stack.last().unwrap().borrow_mut().add_new_variable(session, OYarn::from(name.clone()), &import_result.range); - let mut loc = variable.borrow_mut(); - loc.as_variable_mut().is_import_variable = true; - loc.as_variable_mut().evaluations = Evaluation::from_sections(&symbol, loc_syms); - dep_to_add.push(variable.clone()); + let mut dep_to_add = vec![]; + let sym_type = import_symbol.borrow().typ(); + if sym_type != SymType::COMPILED { + if !Rc::ptr_eq(self.sym_stack.last().unwrap(), &import_symbol) { /*We have to check that the imported symbol is not the current one. It can + happen for example in a .pyi that is importing the .pyd file with the same name. As both exists, odools will try to import the pyi a second time in the same file, + and so create a borrow error here + */ + let symbol = import_symbol.borrow(); + for (name, loc_syms) in symbol.iter_symbols() { + if all_name_allowed || name_filter.contains(&name) { + let variable = self.sym_stack.last().unwrap().borrow_mut().add_new_variable(session, OYarn::from(name.clone()), &import_result.range); + let mut loc = variable.borrow_mut(); + loc.as_variable_mut().is_import_variable = true; + loc.as_variable_mut().evaluations = Evaluation::from_sections(&symbol, loc_syms); + dep_to_add.push(variable.clone()); + } } } } - } - for sym in dep_to_add { - let mut sym_bw = sym.borrow_mut(); - let evaluation = &sym_bw.as_variable_mut().evaluations[0]; - let evaluated_type = &evaluation.symbol; - let evaluated_type = evaluated_type.get_symbol_as_weak(session, &mut None, &mut self.diagnostics, None).weak; - if !evaluated_type.is_expired() { - let evaluated_type = evaluated_type.upgrade().unwrap(); - let evaluated_type_file = evaluated_type.borrow().get_file().unwrap().clone().upgrade().unwrap(); - if !Rc::ptr_eq(&self.file, &evaluated_type_file) { - self.file.borrow_mut().add_dependency(&mut evaluated_type_file.borrow_mut(), self.current_step, BuildSteps::ARCH); + for sym in dep_to_add { + let mut sym_bw = sym.borrow_mut(); + let evaluation = &sym_bw.as_variable_mut().evaluations[0]; + let evaluated_type = &evaluation.symbol; + let evaluated_type = evaluated_type.get_symbol_as_weak(session, &mut None, &mut self.diagnostics, None).weak; + if !evaluated_type.is_expired() { + let evaluated_type = evaluated_type.upgrade().unwrap(); + let evaluated_type_file = evaluated_type.borrow().get_file().unwrap().clone().upgrade().unwrap(); + if !Rc::ptr_eq(&self.file, &evaluated_type_file) { + self.file.borrow_mut().add_dependency(&mut evaluated_type_file.borrow_mut(), self.current_step, BuildSteps::ARCH); + } } } } diff --git a/server/src/core/python_arch_builder_hooks.rs b/server/src/core/python_arch_builder_hooks.rs index 77a95304..dfe3aee2 100644 --- a/server/src/core/python_arch_builder_hooks.rs +++ b/server/src/core/python_arch_builder_hooks.rs @@ -80,7 +80,7 @@ static arch_class_hooks: Lazy> = Lazy::new(|| {vec![ ], func: |session: &mut SessionInfo, _entry_point: &Rc>, symbol: Rc>| { let range = symbol.borrow().range().clone(); - // ----------- env.cr ------------ + // ----------- global ------------ symbol.borrow_mut().add_new_variable(session, Sy!("global"), &range); } }, @@ -240,4 +240,4 @@ impl PythonArchBuilderHooks { } } } -} \ No newline at end of file +} diff --git a/server/src/core/python_arch_eval.rs b/server/src/core/python_arch_eval.rs index b1e35339..82158beb 100644 --- a/server/src/core/python_arch_eval.rs +++ b/server/src/core/python_arch_eval.rs @@ -362,7 +362,7 @@ impl PythonArchEval { let sym_ref_cl = sym_ref.clone(); let syms_followed = Symbol::follow_ref(&EvaluationSymbolPtr::WEAK(EvaluationSymbolWeak::new( Rc::downgrade(&sym_ref_cl), None, false - )), session, &mut None, false, false, None); + )), session, &mut None, false, false, None, None); for sym in syms_followed.iter() { let sym = sym.upgrade_weak(); if let Some(sym) = sym { @@ -397,57 +397,61 @@ impl PythonArchEval { level, &mut Some(&mut self.diagnostics)); - for _import_result in import_results.iter() { - let variable = self.sym_stack.last().unwrap().borrow().get_positioned_symbol(&_import_result.var_name, &_import_result.range); + for import_result in import_results.iter() { + let variable = self.sym_stack.last().unwrap().borrow().get_positioned_symbol(&import_result.var_name, &import_result.range); let Some(variable) = variable.clone() else { continue; }; - if _import_result.found { - let import_sym_ref = _import_result.symbol.clone(); - let has_loop = self.check_for_loop_evaluation(session, import_sym_ref, &variable); - if !has_loop { //anti-loop. We want to be sure we are not evaluating to the same sym - let instance = match _import_result.symbol.borrow().typ() { - SymType::CLASS => Some(false), - _ => None - }; - variable.borrow_mut().set_evaluations(vec![Evaluation::eval_from_symbol(&Rc::downgrade(&_import_result.symbol), instance)]); - let file_of_import_symbol = _import_result.symbol.borrow().get_file(); - if let Some(import_file) = file_of_import_symbol { - let import_file = import_file.upgrade().unwrap(); - if !Rc::ptr_eq(&self.file, &import_file) { - self.file.borrow_mut().add_dependency(&mut import_file.borrow_mut(), self.current_step, BuildSteps::ARCH); + if import_result.found { + variable.borrow_mut().set_evaluations(vec![]); + for import_sym in import_result.symbols.iter() { + let has_loop = self.check_for_loop_evaluation(session, import_sym.clone(), &variable); + if !has_loop { //anti-loop. We want to be sure we are not evaluating to the same sym + let instance = match import_sym.borrow().typ() { + SymType::CLASS => Some(false), + _ => None + }; + variable.borrow_mut().evaluations_mut().unwrap().push(Evaluation::eval_from_symbol(&Rc::downgrade(&import_sym), instance)); + let file_of_import_symbol = import_sym.borrow().get_file(); + if let Some(import_file) = file_of_import_symbol { + let import_file = import_file.upgrade().unwrap(); + if !Rc::ptr_eq(&self.file, &import_file) { + self.file.borrow_mut().add_dependency(&mut import_file.borrow_mut(), self.current_step, BuildSteps::ARCH); + } } - } - } else { - let mut file_tree = _import_result.file_tree.clone(); - file_tree.extend(_import_result.name.split(".").map(|s| oyarn!("{}", s))); - self.file.borrow_mut().not_found_paths_mut().push((self.current_step, file_tree.clone())); - self.entry_point.borrow_mut().not_found_symbols.insert(self.file.clone()); - if self._match_diag_config(session.sync_odoo, &_import_result.symbol) { - if let Some(diagnostic) = create_diagnostic(&session, DiagnosticCode::OLS02002, &[&file_tree.clone().join(".")]) { - self.diagnostics.push(Diagnostic { - range: Range::new(Position::new(_import_result.range.start().to_u32(), 0), Position::new(_import_result.range.end().to_u32(), 0)), - ..diagnostic - }); + } else { + let mut file_tree = import_result.file_tree.clone(); + file_tree.extend(import_result.name.split(".").map(|s| oyarn!("{}", s))); + self.file.borrow_mut().not_found_paths_mut().push((self.current_step, file_tree.clone())); + self.entry_point.borrow_mut().not_found_symbols.insert(self.file.clone()); + if self._match_diag_config(session.sync_odoo, import_sym) { + if let Some(diagnostic) = create_diagnostic(&session, DiagnosticCode::OLS02002, &[&file_tree.clone().join(".")]) { + self.diagnostics.push(Diagnostic { + range: Range::new(Position::new(import_result.range.start().to_u32(), 0), Position::new(import_result.range.end().to_u32(), 0)), + ..diagnostic + }); + } } } } } else { - let mut file_tree = _import_result.file_tree.clone(); - file_tree.extend(_import_result.name.split(".").map(|s| oyarn!("{}", s))); + let mut file_tree = import_result.file_tree.clone(); + file_tree.extend(import_result.name.split(".").map(|s| oyarn!("{}", s))); if session.sync_odoo.config.diag_missing_imports != DiagMissingImportsMode::All && BUILT_IN_LIBS.contains(&file_tree[0].as_str()) { continue; } if !self.safe_import.last().unwrap() { self.file.borrow_mut().not_found_paths_mut().push((self.current_step, file_tree.clone())); self.entry_point.borrow_mut().not_found_symbols.insert(self.file.clone()); - if self._match_diag_config(session.sync_odoo, &_import_result.symbol) { - if let Some(diagnostic) = create_diagnostic(&session, DiagnosticCode::OLS02001, &[&file_tree.clone().join(".")]) { - self.diagnostics.push(Diagnostic { - range: Range::new(Position::new(_import_result.range.start().to_u32(), 0), Position::new(_import_result.range.end().to_u32(), 0)), - ..diagnostic - }); + for import_sym in import_result.symbols.iter() { + if self._match_diag_config(session.sync_odoo, import_sym) { + if let Some(diagnostic) = create_diagnostic(&session, DiagnosticCode::OLS02001, &[&file_tree.clone().join(".")]) { + self.diagnostics.push(Diagnostic { + range: Range::new(Position::new(import_result.range.start().to_u32(), 0), Position::new(import_result.range.end().to_u32(), 0)), + ..diagnostic + }); + } } } } @@ -654,7 +658,7 @@ impl PythonArchEval { } let eval_base = &eval_base[0]; let eval_symbol = eval_base.symbol.get_symbol(session, &mut None, &mut vec![], None); - let ref_sym = Symbol::follow_ref(&eval_symbol, session, &mut None, false, true, None); + let ref_sym = Symbol::follow_ref(&eval_symbol, session, &mut None, false, true, None, None); if ref_sym.len() > 1 { if let Some(diagnostic) = create_diagnostic(&session, DiagnosticCode::OLS01003, &[&AstUtils::flatten_expr(base)]) { self.diagnostics.push(Diagnostic { @@ -817,7 +821,7 @@ impl PythonArchEval { let eval = &eval_iter_node[0]; let eval_symbol = eval.symbol.get_symbol(session, &mut None, &mut vec![], None); if !eval_symbol.is_expired_if_weak() { - let symbol_eval = Symbol::follow_ref(&eval_symbol, session, &mut None, false, false, None); + let symbol_eval = Symbol::follow_ref(&eval_symbol, session, &mut None, false, false, None, None); if symbol_eval.len() == 1 && symbol_eval[0].upgrade_weak().is_some() { let symbol_type_rc = symbol_eval[0].upgrade_weak().unwrap(); let symbol_type = symbol_type_rc.borrow(); @@ -973,7 +977,6 @@ impl PythonArchEval { diagnostics: &mut Vec, ) { if let Some(returns_ann) = func_stmt.returns.as_ref() { - let file_sym = func_sym.borrow().get_file().and_then(|file_weak| file_weak.upgrade()); let mut deps = vec![vec![], vec![]]; let (mut evaluations, diags) = Evaluation::eval_from_ast( session, @@ -983,6 +986,22 @@ impl PythonArchEval { true, &mut deps, ); + // Check for type annotation `typing.Self`, if so, return a `self` evaluation + // And give it priority over other evaluations + if evaluations.iter().any(|evaluation| + Symbol::follow_ref( + &evaluation.symbol.get_symbol(session, &mut None, diagnostics, None), + session, + &mut None, + false, + false, + Some((vec![Sy!("typing")], vec![Sy!("Self")])), + None + ).len() > 0 + ){ + func_sym.borrow_mut().set_evaluations(vec![Evaluation::new_self()]); + return; + } for eval in evaluations.iter_mut() { //as this is an evaluation, we need to set the instance to true match eval.symbol.get_mut_symbol_ptr() { EvaluationSymbolPtr::WEAK(sym_weak) => { @@ -991,23 +1010,10 @@ impl PythonArchEval { _ => {} } } - if file_sym.is_some() { - Symbol::insert_dependencies(&file_sym.as_ref().unwrap(), &mut deps, BuildSteps::ARCH_EVAL); + if let Some(file_sym) = func_sym.borrow().get_file().and_then(|file_weak| file_weak.upgrade()).as_ref() { + Symbol::insert_dependencies(file_sym, &mut deps, BuildSteps::ARCH_EVAL); } diagnostics.extend(diags); - // Check for type annotation `typing.Self`, if so, return a `self` evaluation - let final_evaluations = evaluations.into_iter().map(|eval|{ - let sym_ptrs = Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, diagnostics, None), session, &mut None, false, false, file_sym.clone()); - for sym_ptr in sym_ptrs.iter(){ - let EvaluationSymbolPtr::WEAK(sym_weak) = sym_ptr else {continue}; - let Some(sym_rc) = sym_weak.weak.upgrade() else {continue}; - if sym_rc.borrow().match_tree_from_any_entry(session, &(vec![Sy!("typing")], vec![Sy!("Self")])){ - return Evaluation::new_self(); - } - } - eval - }).collect::>(); - func_sym.borrow_mut().set_evaluations(final_evaluations); } } diff --git a/server/src/core/python_arch_eval_hooks.rs b/server/src/core/python_arch_eval_hooks.rs index 44c69c38..d7bf997d 100644 --- a/server/src/core/python_arch_eval_hooks.rs +++ b/server/src/core/python_arch_eval_hooks.rs @@ -13,6 +13,7 @@ use ruff_text_size::TextRange; use tracing::warn; use weak_table::traits::WeakElement; use crate::core::diagnostics::{create_diagnostic, DiagnosticCode}; +use crate::core::evaluation::GetSymbolHook; use crate::core::odoo::SyncOdoo; use crate::core::evaluation::Context; use crate::core::symbols::symbol::Symbol; @@ -442,8 +443,8 @@ static arch_eval_function_hooks: Lazy> = Lazy::n symbol.borrow_mut().set_evaluations(vec![Evaluation { symbol: EvaluationSymbol::new_with_symbol(Weak::new(), Some(true), - HashMap::from([(S!("hook_name"), ContextValue::STRING(S!("eval_env_get_item")))]), - Some(PythonArchEvalHooks::eval_env_get_item) + HashMap::new(), + Some(GetSymbolHook{callable: PythonArchEvalHooks::eval_env_get_item, name: S!("eval_env_get_item")}) ), value: None, range: None @@ -458,7 +459,7 @@ static arch_eval_function_hooks: Lazy> = Lazy::n symbol: EvaluationSymbol::new_with_symbol(Weak::new(), Some(true), HashMap::new(), - Some(PythonArchEvalHooks::eval_registry_get_item) + Some(GetSymbolHook{callable: PythonArchEvalHooks::eval_registry_get_item, name: S!("eval_registry_get_item")}) ), value: None, range: None @@ -600,7 +601,7 @@ static arch_eval_function_hooks: Lazy> = Lazy::n Rc::downgrade(&fields_class_sym), Some(true), HashMap::new(), - Some(PythonArchEvalHooks::eval_init) + Some(GetSymbolHook{callable: PythonArchEvalHooks::eval_init, name: S!("eval_init")}) ), value: None, range: None, @@ -717,7 +718,7 @@ impl PythonArchEvalHooks { diagnostics.extend(diags); let mut followed_evals = vec![]; for eval in dec_evals { - followed_evals.extend(Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, &mut vec![], None), session, &mut None, true, false, None)); + followed_evals.extend(Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, &mut vec![], None), session, &mut None, true, false, None, None)); } for decorator_eval in followed_evals { let EvaluationSymbolPtr::WEAK(decorator_eval_sym_weak) = decorator_eval else { @@ -874,7 +875,7 @@ impl PythonArchEvalHooks { Rc::downgrade(return_sym.last().unwrap()), Some(true), HashMap::new(), - Some(PythonArchEvalHooks::eval_get) + Some(GetSymbolHook{callable: PythonArchEvalHooks::eval_get, name: S!("eval_get")}) ), value: None, range: None @@ -898,7 +899,7 @@ impl PythonArchEvalHooks { Rc::downgrade(return_sym), Some(true), HashMap::new(), - Some(PythonArchEvalHooks::eval_get) + Some(GetSymbolHook{callable: PythonArchEvalHooks::eval_get, name: S!("eval_get")}) ), value: None, range: None @@ -978,7 +979,7 @@ impl PythonArchEvalHooks { Weak::new(), Some(true), HashMap::new(), - Some(PythonArchEvalHooks::eval_relational) + Some(GetSymbolHook{callable: PythonArchEvalHooks::eval_relational, name: S!("eval_relational")}) ), value: None, range: None, @@ -991,7 +992,7 @@ impl PythonArchEvalHooks { Weak::new(), Some(true), HashMap::new(), - Some(PythonArchEvalHooks::eval_relational) + Some(GetSymbolHook{callable: PythonArchEvalHooks::eval_relational, name: S!("eval_relational")}) ), value: None, range: None, @@ -1108,9 +1109,9 @@ impl PythonArchEvalHooks { Some(true), HashMap::new(), Some(match relational { - Some(oyarn) if oyarn == oyarn!("One2many") => PythonArchEvalHooks::eval_init_relational_one2many, - Some(_) => PythonArchEvalHooks::eval_init_relational, - None => PythonArchEvalHooks::eval_init, + Some(oyarn) if oyarn == oyarn!("One2many") => GetSymbolHook{callable: PythonArchEvalHooks::eval_init_relational_one2many, name: S!("eval_init_relational_one2many")}, + Some(_) => GetSymbolHook{callable: PythonArchEvalHooks::eval_init_relational, name: S!("eval_init_relational")}, + None => GetSymbolHook{callable: PythonArchEvalHooks::eval_init, name: S!("eval_init")}, }) ), value: None, @@ -1293,7 +1294,7 @@ impl PythonArchEvalHooks { Rc::downgrade(&func_sym), Some(true), HashMap::new(), - Some(PythonArchEvalHooks::eval_env_ref) + Some(GetSymbolHook{callable: PythonArchEvalHooks::eval_env_ref, name: S!("eval_env_ref")}) ), value: None, range: None diff --git a/server/src/core/python_odoo_builder.rs b/server/src/core/python_odoo_builder.rs index b6ff7178..3ad5119c 100644 --- a/server/src/core/python_odoo_builder.rs +++ b/server/src/core/python_odoo_builder.rs @@ -448,7 +448,7 @@ impl PythonOdooBuilder { }; for eval in evals.iter() { let eval_sym_ptr = eval.symbol.get_symbol(session, &mut None, &mut vec![], None); - let eval_ptrs = Symbol::follow_ref(&eval_sym_ptr, session, &mut None, true, false, None); + let eval_ptrs = Symbol::follow_ref(&eval_sym_ptr, session, &mut None, true, false, None, None); for eval_ptr in eval_ptrs.iter() { let eval_weak = match &eval_ptr { EvaluationSymbolPtr::WEAK(w) => w, diff --git a/server/src/core/python_validator.rs b/server/src/core/python_validator.rs index 80373bf0..45a45d01 100644 --- a/server/src/core/python_validator.rs +++ b/server/src/core/python_validator.rs @@ -406,7 +406,7 @@ impl PythonValidator { }; for eval in evals.iter() { let symbol = eval.symbol.get_symbol(session, &mut None, &mut vec![], None); - let eval_weaks = Symbol::follow_ref(&symbol, session, &mut None, true, false, None); + let eval_weaks = Symbol::follow_ref(&symbol, session, &mut None, true, false, None, None); for eval_weak in eval_weaks.iter() { let Some(symbol) = eval_weak.upgrade_weak() else {continue}; if !symbol.borrow().is_field_class(session){ @@ -445,7 +445,7 @@ impl PythonValidator { Rc::downgrade(&sym), None, false, - )), session, &mut None, true, true, None); + )), session, &mut None, true, true, None, None); related_eval_weaks.iter().any(|related_eval_weak|{ let Some(related_field_class_sym) = related_eval_weak.upgrade_weak() else { return false @@ -587,6 +587,7 @@ impl PythonValidator { true, false, None, + None, ); comodel_eval_weaks.extend(followed); } diff --git a/server/src/core/symbols/symbol.rs b/server/src/core/symbols/symbol.rs index c6445d2d..e929939a 100644 --- a/server/src/core/symbols/symbol.rs +++ b/server/src/core/symbols/symbol.rs @@ -21,7 +21,7 @@ use weak_table::PtrWeakHashSet; use std::path::PathBuf; use std::rc::{Rc, Weak}; use std::cell::RefCell; -use std::vec; +use std::{u32, vec}; use lsp_types::{Diagnostic, DiagnosticTag, Position, Range, SymbolKind}; use crate::core::symbols::function_symbol::FunctionSymbol; @@ -2196,12 +2196,22 @@ impl Symbol { /* Follow evaluation of current symbol until type, value or end of the chain, depending or the parameters. If a symbol in the chain is a descriptor, return the __get__ return evaluation. + If filter_on_tree is set, stop following when one of the symbols in the chain is in the tree, and only return those symbols. */ - pub fn follow_ref(evaluation: &EvaluationSymbolPtr, session: &mut SessionInfo, context: &mut Option, stop_on_type: bool, stop_on_value: bool, max_scope: Option>>) -> Vec { + pub fn follow_ref(evaluation: &EvaluationSymbolPtr, session: &mut SessionInfo, context: &mut Option, stop_on_type: bool, stop_on_value: bool, filter_on_tree: Option, max_scope: Option>>) -> Vec { + let default_result = match filter_on_tree.as_ref() { + Some(_) => vec![], + None => vec![evaluation.clone()], + }; + let stop_on_tree_syms = filter_on_tree.map(|tree| session.sync_odoo.get_symbol("", &tree, u32::MAX)); + if matches!(stop_on_tree_syms.as_ref(), Some(syms) if syms.is_empty()) { + // can't find the tree symbol, stop here + return default_result; + } match evaluation { EvaluationSymbolPtr::WEAK(w) => { let Some(symbol) = w.weak.upgrade() else { - return vec![evaluation.clone()]; + return default_result; }; if stop_on_value { if let Some(evals) = symbol.borrow().evaluations() { @@ -2215,7 +2225,7 @@ impl Symbol { //return a list of all possible evaluation: a weak ptr to the final symbol, and a bool indicating if this is an instance or not let mut results = Symbol::next_refs(session, symbol.clone(), context, &w.context, stop_on_type, &mut vec![]); if results.is_empty() { - return vec![evaluation.clone()]; + return default_result; } if w.instance.is_some_and(|v| v) { //if the previous evaluation was set to True, we want to keep it @@ -2262,6 +2272,11 @@ impl Symbol { if max_scope.is_some() && !sym.has_rc_in_parents(max_scope.as_ref().unwrap().clone(), true) { continue; } + if let Some(stop_on_tree_syms) = stop_on_tree_syms.as_ref() { + if stop_on_tree_syms.iter().any(|s| Rc::ptr_eq(s, &sym_rc)) { + continue; + } + } } if sym_rc.borrow().as_variable().evaluations.is_empty() && sym_rc.borrow().name() != "__all__" && can_eval_external { //no evaluation? let's check that the file has been evaluated @@ -2307,6 +2322,20 @@ impl Symbol { _ => {} } } + if let Some(stop_on_tree_syms) = stop_on_tree_syms.as_ref() { + results.retain(|r| { + match r { + EvaluationSymbolPtr::WEAK(weak) => { + if let Some(sym_rc) = weak.weak.upgrade() { + stop_on_tree_syms.iter().any(|s| Rc::ptr_eq(s, &sym_rc)) + } else { + false + } + }, + _ => false + } + }); + } Vec::from(results) // :'( a whole copy? }, _ => { @@ -2606,7 +2635,7 @@ impl Symbol { if let Some(evals) = self.evaluations().as_ref() { for eval in evals.iter() { let symbol = eval.symbol.get_symbol(session, &mut None, &mut vec![], None); - let eval_weaks = Symbol::follow_ref(&symbol, session, &mut None, true, false, None); + let eval_weaks = Symbol::follow_ref(&symbol, session, &mut None, true, false, None, None); for eval_weak in eval_weaks.iter() { if let Some(symbol) = eval_weak.upgrade_weak() { if symbol.borrow().is_field_class(session){ @@ -2629,7 +2658,7 @@ impl Symbol { if let Some(evals) = self.evaluations().as_ref() { for eval in evals.iter() { let symbol = eval.symbol.get_symbol(session, &mut None, &mut vec![], None); - let eval_weaks = Symbol::follow_ref(&symbol, session, &mut None, true, false, None); + let eval_weaks = Symbol::follow_ref(&symbol, session, &mut None, true, false, None, None); for eval_weak in eval_weaks.iter() { if let Some(symbol) = eval_weak.upgrade_weak() { if symbol.borrow().typ() == SymType::FUNCTION { @@ -2742,7 +2771,7 @@ impl Symbol { if let Some(evals) = self.evaluations().as_ref() { for eval in evals.iter() { let symbol = eval.symbol.get_symbol(session, &mut None, &mut vec![], None); - let eval_weaks = Symbol::follow_ref(&symbol, session, &mut None, true, false, None); + let eval_weaks = Symbol::follow_ref(&symbol, session, &mut None, true, false, None, None); for eval_weak in eval_weaks.iter() { if let Some(symbol) = eval_weak.upgrade_weak() { if symbol.borrow().is_specific_field_class(session, field_names){ diff --git a/server/src/core/symbols/variable_symbol.rs b/server/src/core/symbols/variable_symbol.rs index a28fc69a..68450eb2 100644 --- a/server/src/core/symbols/variable_symbol.rs +++ b/server/src/core/symbols/variable_symbol.rs @@ -67,7 +67,7 @@ impl VariableSymbol { context = Some(HashMap::new()); context.as_mut().unwrap().insert(S!("base_attr"), ContextValue::SYMBOL(parent.clone())); } - let eval_weaks = Symbol::follow_ref(&symbol, session, &mut context, false, false, None); + let eval_weaks = Symbol::follow_ref(&symbol, session, &mut context, false, false, None, None); for eval_weak in eval_weaks.iter() { if let Some(symbol) = eval_weak.upgrade_weak() { if ["Many2one", "One2many", "Many2many"].contains(&symbol.borrow().name().as_str()) { diff --git a/server/src/core/xml_validation.rs b/server/src/core/xml_validation.rs index 31610ad9..a0ec8cce 100644 --- a/server/src/core/xml_validation.rs +++ b/server/src/core/xml_validation.rs @@ -3,7 +3,7 @@ use std::{cell::RefCell, cmp::Ordering, collections::{HashMap, HashSet}, rc::Rc} use lsp_types::{Diagnostic, Position, Range}; use tracing::{info, trace}; -use crate::{Sy, constants::{BuildSteps, DEBUG_STEPS, OYarn}, core::{diagnostics::{DiagnosticCode, create_diagnostic}, entry_point::{EntryPoint, EntryPointType}, evaluation::ContextValue, file_mgr::{FileInfo, FileMgr}, model::Model, odoo::SyncOdoo, symbols::{module_symbol::ModuleSymbol, symbol::Symbol}, xml_data::{OdooData, OdooDataRecord, XmlDataDelete, XmlDataMenuItem, XmlDataTemplate}}, oyarn, threads::SessionInfo, utils::compare_semver}; +use crate::{Sy, constants::{BuildSteps, DEBUG_STEPS, OYarn}, core::{diagnostics::{DiagnosticCode, create_diagnostic}, entry_point::{EntryPoint, EntryPointType}, evaluation::ContextValue, file_mgr::FileInfo, model::Model, odoo::SyncOdoo, symbols::symbol::Symbol, xml_data::{OdooData, OdooDataRecord, XmlDataDelete, XmlDataMenuItem, XmlDataTemplate}}, oyarn, threads::SessionInfo, utils::compare_semver}; diff --git a/server/src/features/ast_utils.rs b/server/src/features/ast_utils.rs index 1e5fd505..70e1753c 100644 --- a/server/src/features/ast_utils.rs +++ b/server/src/features/ast_utils.rs @@ -113,9 +113,9 @@ impl AstUtils { //we import as a from_stmt, to refuse import of variables, as the import stmt is not complete let to_analyze = Identifier { id: Name::new(to_analyze), range: TextRange::new(TextSize::new(0), TextSize::new(0)), node_index: AtomicNodeIndex::default() }; let (from_symbol, _fallback_sym, _file_tree) = resolve_from_stmt(session, file_symbol, Some(&to_analyze), 0); - if let Some(symbol) = from_symbol { + if let Some(symbols) = from_symbol { let result = AnalyzeAstResult { - evaluations: vec![Evaluation::eval_from_symbol(&Rc::downgrade(&symbol), None)], + evaluations: symbols.iter().map(|symbol| Evaluation::eval_from_symbol(&Rc::downgrade(symbol), None)).collect(), diagnostics: vec![], }; return Some((result, Some(range))); @@ -131,8 +131,8 @@ impl AstUtils { let res = res.into_iter().filter(|s| s.found).collect::>(); if !res.is_empty() { let result = AnalyzeAstResult { - evaluations: res.iter().map( - |s| Evaluation::eval_from_symbol(&Rc::downgrade(&s.symbol), None) + evaluations: res.iter().flat_map( + |s| s.symbols.iter().map(|symbol| Evaluation::eval_from_symbol(&Rc::downgrade(symbol), None)) ).collect(), diagnostics: vec![], }; @@ -162,9 +162,9 @@ impl AstUtils { }; let to_analyze = Identifier { id: Name::new(to_analyze), range: TextRange::new(TextSize::new(0), TextSize::new(0)), node_index: AtomicNodeIndex::default() }; let (from_symbol, _fallback_sym, _file_tree) = resolve_from_stmt(session, file_symbol, Some(&to_analyze), 0); - if let Some(symbol) = from_symbol { + if let Some(symbols) = from_symbol { let result = AnalyzeAstResult { - evaluations: vec![Evaluation::eval_from_symbol(&Rc::downgrade(&symbol), None)], + evaluations: symbols.iter().map(|symbol| Evaluation::eval_from_symbol(&Rc::downgrade(symbol), None)).collect(), diagnostics: vec![], }; return Some((result, Some(range))); diff --git a/server/src/features/completion.rs b/server/src/features/completion.rs index f62ec51e..3b5312df 100644 --- a/server/src/features/completion.rs +++ b/server/src/features/completion.rs @@ -546,7 +546,9 @@ fn complete_decorator_call( let dec_evals = Evaluation::eval_from_ast(session, &decorator_base, scope.clone(), max_infer, false, &mut vec![]).0; let mut followed_evals = vec![]; for eval in dec_evals { - followed_evals.extend(Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, &mut vec![], None), session, &mut None, true, false, None)); + followed_evals.extend( + Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, &mut vec![], None), session, &mut None, true, false, None, None) + ); } for decorator_eval in followed_evals{ let EvaluationSymbolPtr::WEAK(decorator_eval_sym_weak) = decorator_eval else { @@ -821,7 +823,7 @@ fn complete_attribut(session: &mut SessionInfo, file: &Rc>, attr //TODO shouldn't we set and clean context here? let parent_sym_eval = parent_eval.symbol.get_symbol(session, &mut None, &mut vec![], Some(scope.clone())); if !parent_sym_eval.is_expired_if_weak() { - let parent_sym_types = Symbol::follow_ref(&parent_sym_eval, session, &mut None, false, false, None); + let parent_sym_types = Symbol::follow_ref(&parent_sym_eval, session, &mut None, false, false, None, None); for parent_sym_type in parent_sym_types.iter() { let Some(parent_sym) = parent_sym_type.upgrade_weak() else {continue}; add_model_attributes(session, &mut items, from_module.clone(), parent_sym, parent_sym_eval.as_weak().is_super, false, false, attr.attr.id.as_str(), &None) @@ -842,7 +844,7 @@ fn complete_subscript(session: &mut SessionInfo, file: &Rc>, exp for eval in subscripted.iter() { let eval_symbol = eval.symbol.get_symbol(session, &mut None, &mut vec![], Some(scope.clone())); if !eval_symbol.is_expired_if_weak() { - let symbol_types = Symbol::follow_ref(&eval_symbol, session, &mut None, false, false, None); + let symbol_types = Symbol::follow_ref(&eval_symbol, session, &mut None, false, false, None, None); for symbol_type in symbol_types.iter() { if let Some(symbol_type) = symbol_type.upgrade_weak() { let borrowed = symbol_type.borrow(); @@ -851,10 +853,8 @@ fn complete_subscript(session: &mut SessionInfo, file: &Rc>, exp if get_item.borrow().evaluations().as_ref().unwrap().len() == 1 { let get_item_bw = get_item.borrow(); let get_item_eval = get_item_bw.evaluations().as_ref().unwrap().first().unwrap(); - if let EvaluationSymbolPtr::WEAK(w) = get_item_eval.symbol.get_symbol_ptr() { - if matches!(w.context.get(&S!("hook_name")), Some(hook_name) if hook_name.as_string() == "eval_env_get_item") { - return complete_expr(&expr_subscript.slice, session, file, offset, is_param, &vec![ExpectedType::MODEL_NAME]); - } + if get_item_eval.symbol.get_symbol_hook.as_ref().map(|hook| &hook.name == "eval_env_get_item").unwrap_or_default(){ + return complete_expr(&expr_subscript.slice, session, file, offset, is_param, &vec![ExpectedType::MODEL_NAME]); } } } @@ -1106,7 +1106,7 @@ fn build_completion_item_from_symbol(session: &mut SessionInfo, symbols: Vec>(); let type_details = typ.iter().map(|eval| FeaturesUtils::get_inferred_types(session, eval, &mut Some(context_of_symbol.clone()), &symbols[0].borrow().typ()) @@ -1186,4 +1186,4 @@ fn get_completion_item_kind(typ: &SymType) -> CompletionItemKind { SymType::XML_FILE => CompletionItemKind::FILE, SymType::CSV_FILE => CompletionItemKind::FILE, } -} \ No newline at end of file +} diff --git a/server/src/features/definition.rs b/server/src/features/definition.rs index 6b6b8ea8..aef6111c 100644 --- a/server/src/features/definition.rs +++ b/server/src/features/definition.rs @@ -185,7 +185,7 @@ impl DefinitionFeature { return; }; let (analyse_ast_result, _range) = AstUtils::get_symbol_from_expr(session, file_symbol, &crate::core::evaluation::ExprOrIdent::Expr(&attr_expr.value), offset as u32); - let eval_ptrs = analyse_ast_result.evaluations.iter().flat_map(|eval| Symbol::follow_ref(eval.symbol.get_symbol_ptr(), session, &mut None, false, false, None)).collect::>(); + let eval_ptrs = analyse_ast_result.evaluations.iter().flat_map(|eval| Symbol::follow_ref(eval.symbol.get_symbol_ptr(), session, &mut None, false, false, None, None)).collect::>(); let maybe_module = file_symbol.borrow().find_module(); let symbols = eval_ptrs.iter().flat_map(|eval_ptr| { let Some(symbol) = eval_ptr.upgrade_weak() else { diff --git a/server/src/features/features_utils.rs b/server/src/features/features_utils.rs index fe59572c..f5d6d74e 100644 --- a/server/src/features/features_utils.rs +++ b/server/src/features/features_utils.rs @@ -77,7 +77,17 @@ impl FeaturesUtils { let evaluations = Evaluation::eval_from_ast(session, &call_expr.func, scope.clone(), &call_expr.func.range().start(), false, &mut vec![]).0; let mut followed_evals = vec![]; for eval in evaluations { - followed_evals.extend(Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, &mut vec![], None), session, &mut None, true, false, None)); + followed_evals.extend( + Symbol::follow_ref( + &eval.symbol.get_symbol(session, &mut None, &mut vec![], None), + session, + &mut None, + true, + false, + None, + None, + ) + ); } if !followed_evals.iter().any(|eval| eval.is_weak() && eval.as_weak().weak.upgrade().map(|sym| sym.borrow().is_field_class(session)).unwrap_or(false) @@ -221,7 +231,17 @@ impl FeaturesUtils { let callable_evals = Evaluation::eval_from_ast(session, &call_expr.func, scope.clone(), &call_expr.func.range().start(), false, &mut vec![]).0; let mut followed_evals = vec![]; for eval in callable_evals { - followed_evals.extend(Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, &mut vec![], None), session, &mut None, true, false, None)); + followed_evals.extend( + Symbol::follow_ref( + &eval.symbol.get_symbol(session, &mut None, &mut vec![], None), + session, + &mut None, + true, + false, + None, + None, + ) + ); } for callable_eval in followed_evals { let EvaluationSymbolPtr::WEAK(callable) = callable_eval else { @@ -294,7 +314,17 @@ impl FeaturesUtils { let callable_evals = Evaluation::eval_from_ast(session, &call_expr.func, scope.clone(), &call_expr.func.range().start(), false, &mut vec![]).0; let mut followed_evals = vec![]; for eval in callable_evals { - followed_evals.extend(Symbol::follow_ref(&eval.symbol.get_symbol(session, &mut None, &mut vec![], None), session, &mut None, true, false, None)); + followed_evals.extend( + Symbol::follow_ref( + &eval.symbol.get_symbol(session, &mut None, &mut vec![], None), + session, + &mut None, + true, + false, + None, + None, + ) + ); } for callable_eval in followed_evals { let EvaluationSymbolPtr::WEAK(callable) = callable_eval else { @@ -487,7 +517,7 @@ impl FeaturesUtils { continue; }; let mut context = Some(eval_symbol.as_weak().context.clone()); - let evaluation_ptrs = Symbol::follow_ref(&eval_symbol, session, &mut context, false, false, None); + let evaluation_ptrs = Symbol::follow_ref(&eval_symbol, session, &mut context, false, false, None, None); let symbol_type = symbol.borrow().typ(); let symbol_name = symbol.borrow().name().clone(); @@ -581,7 +611,7 @@ impl FeaturesUtils { Some(func_eval) => { let type_names: Vec<_> = func_eval.iter().flat_map(|eval|{ let eval_symbol = eval.symbol.get_symbol_weak_transformed(session, context, &mut vec![], None); - let weak_eval_symbols = Symbol::follow_ref(&eval_symbol, session, context, true, false, None); + let weak_eval_symbols = Symbol::follow_ref(&eval_symbol, session, context, true, false, None, None); weak_eval_symbols.iter().map(|weak_eval_symbol| match weak_eval_symbol.upgrade_weak(){ //if fct is a variable, it means that evaluation is None. Some(s_type) if s_type.borrow().typ() != SymType::VARIABLE => s_type.borrow().name().to_string(), @@ -736,4 +766,4 @@ impl FeaturesUtils { } " \n" } -} \ No newline at end of file +} diff --git a/server/src/utils.rs b/server/src/utils.rs index f63f61cd..6e9e585b 100644 --- a/server/src/utils.rs +++ b/server/src/utils.rs @@ -158,10 +158,9 @@ pub trait PathSanitizer { impl PathSanitizer for PathBuf { fn sanitize(&self) -> String { - let mut path = self.to_slash_lossy().to_string(); - #[cfg(windows)] { + let mut path = self.to_slash_lossy().to_string(); // check if path begins with //?/ if yes remove it // to handle extended-length path prefix // https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation @@ -173,9 +172,11 @@ impl PathSanitizer for PathBuf { let disk_letter = path.chars().next().unwrap().to_ascii_lowercase(); path.replace_range(0..1, &disk_letter.to_string()); } + return path; } - path + #[cfg(not(windows))] + return self.to_slash_lossy().to_string(); } /// Convert the path to a tree structure. @@ -204,10 +205,9 @@ impl PathSanitizer for PathBuf { impl PathSanitizer for Path { fn sanitize(&self) -> String { - let mut path = self.to_slash_lossy().to_string(); - #[cfg(windows)] { + let mut path = self.to_slash_lossy().to_string(); if path.starts_with("\\\\?\\") { path = path[4..].to_string(); } @@ -216,9 +216,11 @@ impl PathSanitizer for Path { let disk_letter = path.chars().next().unwrap().to_ascii_lowercase(); path.replace_range(0..1, &disk_letter.to_string()); } + return path; } - path + #[cfg(not(windows))] + return self.to_slash_lossy().to_string(); } fn to_tree(&self) -> Tree { diff --git a/server/tests/test_utils.rs b/server/tests/test_utils.rs index 11d1ea40..dcfec358 100644 --- a/server/tests/test_utils.rs +++ b/server/tests/test_utils.rs @@ -77,11 +77,6 @@ pub fn verify_diagnostics_against_doc( let mut diags: HashMap> = HashMap::new(); for diag in &diagnostics { let line = diag.range.start.line; - let code_str = match &diag.code { - Some(NumberOrString::String(c)) => c.clone(), - Some(NumberOrString::Number(n)) => n.to_string(), - None => continue, - }; diags.entry(line).or_default().push(diag); }