Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Diagnostics and autocompletion for search domains #161

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions server/error_code.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,22 @@ key parsing error
"Module XXXX depends on YYYY which is not found. Please review your addons paths".
The module XXXX create a dependency on YYYY, but this module is not found with the current addon path.

### OLS30311

"Domains should be a list of tuples".
The provided domain is not a list of tuples. A domain should be in the form [("field", "operator", "value")]

### OLS30312

"Domain tuple should have 3 elements".
Tuples in a domain should contains 3 elements: ("field", "operator", "value")

### OLS30313

"XXX takes Y positional arguments but Z was given".
Number of positional arguments given as parameter to the function is wrong.

### OLS30314

"XXX got an unexpected keyword argument 'YYY'".
You gave a named parameter that is not present in the function definition.
222 changes: 218 additions & 4 deletions server/src/core/evaluation.rs

Large diffs are not rendered by default.

43 changes: 39 additions & 4 deletions server/src/core/python_arch_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::utils::PathSanitizer as _;
use crate::S;

use super::import_resolver::ImportResult;
use super::symbols::function_symbol::Argument;
use super::symbols::function_symbol::{Argument, ArgumentType};


#[derive(Debug)]
Expand Down Expand Up @@ -365,14 +365,49 @@ impl PythonArchBuilder {
}
drop(sym_bw);
//add params
for arg in func_def.parameters.posonlyargs.iter().chain(&func_def.parameters.args) {
for arg in func_def.parameters.posonlyargs.iter() {
let param = sym.borrow_mut().add_new_variable(session, &arg.parameter.name.id.to_string(), &arg.range);
param.borrow_mut().as_variable_mut().is_parameter = true;
sym.borrow_mut().as_func_mut().args.push(Argument {
symbol: Rc::downgrade(&param),
default_value: None,
is_args: false,
is_kwargs: false,
arg_type: ArgumentType::POS_ONLY
});
}
for arg in func_def.parameters.args.iter() {
let param = sym.borrow_mut().add_new_variable(session, &arg.parameter.name.id.to_string(), &arg.range);
param.borrow_mut().as_variable_mut().is_parameter = true;
sym.borrow_mut().as_func_mut().args.push(Argument {
symbol: Rc::downgrade(&param),
default_value: None,
arg_type: ArgumentType::ARG
});
}
if let Some(arg) = &func_def.parameters.vararg {
let param = sym.borrow_mut().add_new_variable(session, &arg.name.id.to_string(), &arg.range);
param.borrow_mut().as_variable_mut().is_parameter = true;
sym.borrow_mut().as_func_mut().args.push(Argument {
symbol: Rc::downgrade(&param),
default_value: None,
arg_type: ArgumentType::VARARG
});
}
for arg in func_def.parameters.kwonlyargs.iter() {
let param = sym.borrow_mut().add_new_variable(session, &arg.parameter.name.id.to_string(), &arg.range);
param.borrow_mut().as_variable_mut().is_parameter = true;
sym.borrow_mut().as_func_mut().args.push(Argument {
symbol: Rc::downgrade(&param),
default_value: None,
arg_type: ArgumentType::KWORD_ONLY
});
}
if let Some(arg) = &func_def.parameters.kwarg {
let param = sym.borrow_mut().add_new_variable(session, &arg.name.id.to_string(), &arg.range);
param.borrow_mut().as_variable_mut().is_parameter = true;
sym.borrow_mut().as_func_mut().args.push(Argument {
symbol: Rc::downgrade(&param),
default_value: None,
arg_type: ArgumentType::KWARG
});
}
//visit body
Expand Down
15 changes: 15 additions & 0 deletions server/src/core/python_arch_eval_hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ static arch_eval_file_hooks: Lazy<Vec<PythonArchEvalFileHook>> = Lazy::new(|| {v
let range = id.range().clone();
id.set_evaluations(vec![Evaluation::new_list(odoo, values, range.clone())]);
}},
/*PythonArchEvalFileHook { file_tree: vec![S!("odoo"), S!("models")],
content_tree: vec![S!("BaseModel"), S!("search_count")],
if_exist_only: true,
func: |odoo: &mut SyncOdoo, _file_symbol: Rc<RefCell<Symbol>>, symbol: Rc<RefCell<Symbol>>| {
let values: Vec<ruff_python_ast::Expr> = Vec::new();
let mut id = symbol.borrow_mut();
let range = id.range().clone();
id.set_evaluations(vec![Evaluation::eval_from_symbol(odoo, values, range.clone())]);
}},*/
PythonArchEvalFileHook {file_tree: vec![S!("odoo"), S!("api")],
content_tree: vec![S!("Environment"), S!("cr")],
if_exist_only: true,
Expand Down Expand Up @@ -308,6 +317,12 @@ static arch_eval_function_hooks: Lazy<Vec<PythonArchEvalFunctionHook>> = Lazy::n
range: None,
value: None
});
let func = search.as_func_mut();
if func.args.len() == 6 {
if let Some(arg_symbol) = func.args.get(1).unwrap().symbol.upgrade() {
arg_symbol.borrow_mut().set_evaluations(vec![Evaluation::new_domain(odoo)]);
}
}
}},
PythonArchEvalFunctionHook { tree: (vec![S!("odoo"), S!("models")], vec![S!("BaseModel"), S!("browse")]),
if_exist_only: true,
Expand Down
31 changes: 25 additions & 6 deletions server/src/core/python_validator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use ruff_python_ast::{Alias, Expr, Identifier, Stmt, StmtAnnAssign, StmtAssign, StmtClassDef, StmtTry};
use ruff_text_size::{Ranged, TextRange};
use ruff_text_size::{Ranged, TextRange, TextSize};
use tracing::{trace, warn};
use std::rc::Rc;
use std::cell::RefCell;
Expand Down Expand Up @@ -168,20 +168,34 @@ impl PythonValidator {
self.visit_ann_assign(session, a);
},
Stmt::Expr(e) => {
let (eval, diags) = Evaluation::eval_from_ast(session, &e.value, self.sym_stack.last().unwrap().clone(), &e.range.start());
self.diagnostics.extend(diags);
self.validate_expr(session, &e.value, &e.value.start());
},
Stmt::If(i) => {
self.validate_expr(session, &i.test, &i.test.start());
self.validate_body(session, &i.body);
for elses in i.elif_else_clauses.iter() {
if let Some(test) = &elses.test {
self.validate_expr(session, test, &test.start());
}
self.validate_body(session, &elses.body);
}
},
Stmt::Break(_) => {},
Stmt::Continue(_) => {},
Stmt::Delete(_) => {
//TODO
Stmt::Delete(d) => {
for target in d.targets.iter() {
self.validate_expr(session, target, &target.start());
}
},
Stmt::For(f) => {
//TODO check condition ? if some checks has to be done on single Expr
self.validate_expr(session, &f.target, &f.target.start());
self.validate_body(session, &f.body);
self.validate_body(session, &f.orelse);
},
Stmt::While(w) => {
self.validate_expr(session, &w.test, &w.test.start());
self.validate_body(session, &w.body);
self.validate_body(session, &w.orelse);
},
Stmt::Return(r) => {},
_ => {
Expand Down Expand Up @@ -356,4 +370,9 @@ impl PythonValidator {
//TODO do we want to raise something?
}
}

fn validate_expr(&mut self, session: &mut SessionInfo, expr: &Expr, max_infer: &TextSize) {
let (eval, diags) = Evaluation::eval_from_ast(session, &expr, self.sym_stack.last().unwrap().clone(), &max_infer);
self.diagnostics.extend(diags);
}
}
14 changes: 11 additions & 3 deletions server/src/core/symbols/function_symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@ use crate::{constants::{BuildStatus, BuildSteps}, core::evaluation::{Context, Ev

use super::{symbol::Symbol, symbol_mgr::{SectionRange, SymbolMgr}};

#[derive(Debug, PartialEq)]
pub enum ArgumentType {
POS_ONLY,
ARG,
KWARG,
VARARG,
KWORD_ONLY,
}

#[derive(Debug)]
pub struct Argument {
pub symbol: Weak<RefCell<Symbol>>, //always a weak to a symbol of the function
//other informations about arg
pub default_value: Option<Evaluation>,
pub is_args: bool,
pub is_kwargs: bool,
pub arg_type: ArgumentType,
}

#[derive(Debug)]
Expand Down Expand Up @@ -107,7 +115,7 @@ impl FunctionSymbol {

pub fn can_be_in_class(&self) -> bool {
for arg in self.args.iter() {
if !arg.is_kwargs && !arg.is_args { //is_args is technically false, as func(*self) is possible, but reaaaaally weird, so let's assume nobody do that
if arg.arg_type != ArgumentType::KWARG && arg.arg_type != ArgumentType::KWORD_ONLY {
return true;
}
}
Expand Down
1 change: 1 addition & 0 deletions server/src/core/symbols/variable_symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ impl VariableSymbol {
}

pub fn is_type_alias(&self) -> bool {
//TODO it does not use get_symbol call, and only evaluate "sym" from EvaluationSymbol
return self.evaluations.len() >= 1 && self.evaluations.iter().all(|x| !x.symbol.is_instance().unwrap_or(true)) && !self.is_import_variable;
}

Expand Down
2 changes: 1 addition & 1 deletion server/src/features/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,7 @@ fn build_completion_item_from_symbol(session: &mut SessionInfo, symbol: &Rc<RefC
documentation: Some(
lsp_types::Documentation::MarkupContent(MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: HoverFeature::build_markdown_description(session, &vec![Evaluation::eval_from_symbol(&Rc::downgrade(symbol))])
value: HoverFeature::build_markdown_description(session, None, &vec![Evaluation::eval_from_symbol(&Rc::downgrade(symbol))])
})),
..Default::default()
}
Expand Down
56 changes: 53 additions & 3 deletions server/src/features/hover.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use ruff_python_ast::Expr;
use ruff_text_size::TextRange;
use lsp_types::{Hover, HoverContents, MarkupContent, Range};
use weak_table::traits::WeakElement;
use crate::core::evaluation::{AnalyzeAstResult, Context, Evaluation};
use crate::core::evaluation::{AnalyzeAstResult, Context, Evaluation, EvaluationValue};
use crate::core::file_mgr::{FileInfo, FileMgr};
use crate::threads::SessionInfo;
use crate::utils::PathSanitizer as _;
use std::collections::HashSet;
use std::path::PathBuf;
use std::rc::{Rc, Weak};
use std::u32;
use crate::core::symbols::symbol::Symbol;
use crate::constants::*;
use crate::features::ast_utils::AstUtils;
Expand All @@ -32,7 +34,7 @@ impl HoverFeature {
return Some(Hover { contents:
HoverContents::Markup(MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: HoverFeature::build_markdown_description(session, &evals)
value: HoverFeature::build_markdown_description(session, Some(file_symbol.clone()), &evals)
}),
range: range
});
Expand Down Expand Up @@ -170,7 +172,7 @@ impl HoverFeature {
value
}

pub fn build_markdown_description(session: &mut SessionInfo, evals: &Vec<Evaluation>) -> String {
pub fn build_markdown_description(session: &mut SessionInfo, file_symbol: Option<Rc<RefCell<Symbol>>>, evals: &Vec<Evaluation>) -> String {
//let eval = &evals[0]; //TODO handle more evaluations
let mut value = S!("");
for (index, eval) in evals.iter().enumerate() {
Expand All @@ -184,6 +186,54 @@ impl HoverFeature {
let symbol = symbol.upgrade().unwrap();
let mut context = Some(eval.symbol.context.clone());
let type_refs = Symbol::follow_ref(&symbol, session, &mut context, true, false, None, &mut vec![]);
//search for a constant evaluation like a model name
if let Some(eval_value) = eval.value.as_ref() {
match eval_value {
crate::core::evaluation::EvaluationValue::CONSTANT(ruff_python_ast::Expr::StringLiteral(expr)) => {
let str = expr.value.to_string();
let model = session.sync_odoo.models.get(&str).cloned();
if let Some(model) = model {
if let Some(file_symbol) = file_symbol.as_ref() {
let from_module = file_symbol.borrow().find_module();
let main_class = model.borrow().get_main_symbols(session, from_module.clone(), &mut None);
for main_class in main_class.iter() {
let main_class = main_class.borrow();
let main_class_module = main_class.find_module();
if let Some(main_class_module) = main_class_module {
value += format!("Model in {}: {} \n", main_class_module.borrow().name(), main_class.name()).as_str();
if main_class.doc_string().is_some() {
value = value + " \n*** \n" + main_class.doc_string().as_ref().unwrap();
}
let mut other_imps = model.borrow().all_symbols(session, from_module.clone());
other_imps.sort_by(|x, y| {
if x.1.is_none() && y.1.is_some() {
return std::cmp::Ordering::Less;
} else if x.1.is_some() && y.1.is_none() {
return std::cmp::Ordering::Greater;
} else {
return x.0.borrow().find_module().unwrap().borrow().name().cmp(&y.0.borrow().find_module().unwrap().borrow().name());
}
});
value += format!(" \n*** \n").as_str();
for other_imp in other_imps.iter() {
let mod_name = other_imp.0.borrow().find_module().unwrap().borrow().name().clone();
if other_imp.1.is_none() {
value += format!("inherited in {} \n", mod_name).as_str();
} else {
value += format!("inherited in {} (require {}) \n", mod_name, other_imp.1.as_ref().unwrap()).as_str();
}
}
}
}
continue;
}
}
continue;
},
_ => {
}
}
}
// BLOCK 1: (type) **name** -> infered_type
value += HoverFeature::build_block_1(session, &symbol, &type_refs, &mut context).as_str();
// BLOCK 2: useful links
Expand Down