use cairo_lang_defs::ids::{NamedLanguageElementId, TraitFunctionId};
use cairo_lang_filesystem::db::{CORELIB_CRATE_NAME, FilesGroup};
use cairo_lang_filesystem::ids::{CrateId, CrateLongId, SmolStrId};
use cairo_lang_semantic::db::SemanticGroup;
use cairo_lang_semantic::expr::inference::infers::InferenceEmbeddings;
use cairo_lang_semantic::expr::inference::solver::SolutionSet;
use cairo_lang_semantic::expr::inference::{ImplVarTraitItemMappings, InferenceId};
use cairo_lang_semantic::items::function_with_body::{
FunctionWithBodySemantic, SemanticExprLookup,
};
use cairo_lang_semantic::lookup_item::LookupItemEx;
use cairo_lang_semantic::lsp_helpers::{LspHelpers, TypeFilter};
use cairo_lang_semantic::resolve::Resolver;
use cairo_lang_syntax::node::{SyntaxNode, TypedStablePtr, TypedSyntaxNode, ast};
use cairo_lang_utils::Intern;
use itertools::{Itertools, chain};
use tracing::debug;
use crate::lang::db::{AnalysisDatabase, LsSemanticGroup};
pub fn find_methods_for_type<'db>(
db: &'db AnalysisDatabase,
resolver: &mut Resolver<'db>,
ty: cairo_lang_semantic::TypeId<'db>,
stable_ptr: cairo_lang_syntax::node::ids::SyntaxStablePtrId<'db>,
) -> Vec<TraitFunctionId<'db>> {
let type_filter = match ty.head(db) {
Some(head) => TypeFilter::TypeHead(head),
None => TypeFilter::NoFilter,
};
let mut relevant_methods = Vec::new();
let crate_config = db.crate_config(resolver.owning_crate_id);
let default = &Default::default();
let dependencies = crate_config.map(|config| &config.settings.dependencies).unwrap_or(default);
for crate_id in chain!(
[resolver.owning_crate_id],
(!dependencies.contains_key(CORELIB_CRATE_NAME)).then(|| CrateId::core(db)),
dependencies.iter().map(|(name, setting)| {
CrateLongId::Real {
name: SmolStrId::from(db, name.as_str()),
discriminator: setting.discriminator.clone(),
}
.intern(db)
})
) {
let methods = db.methods_in_crate(crate_id, type_filter.clone());
for trait_function in methods.iter().copied() {
let clone_data =
&mut resolver.inference().clone_with_inference_id(db, InferenceId::NoContext);
let mut inference = clone_data.inference(db);
let lookup_context = resolver.impl_lookup_context();
let Some((concrete_trait_id, _)) = inference.infer_concrete_trait_by_self(
trait_function,
ty,
lookup_context,
Some(stable_ptr),
&mut vec![],
) else {
debug!("can't fit");
continue;
};
inference.solve().ok();
if !matches!(
inference.trait_solution_set(
concrete_trait_id,
ImplVarTraitItemMappings::default(),
lookup_context
),
Ok(SolutionSet::Unique(_) | SolutionSet::Ambiguous(_))
) {
continue;
}
relevant_methods.push(trait_function);
}
}
relevant_methods.iter().unique().copied().collect()
}
pub fn available_traits_for_method<'db>(
db: &'db AnalysisDatabase,
node: &SyntaxNode<'db>,
resolver: &mut Resolver<'db>,
) -> Option<Vec<String>> {
let stable_ptr = node.ancestor_of_type::<ast::ExprBinary>(db)?.lhs(db).stable_ptr(db);
let function_with_body = db.find_lookup_item(*node)?.function_with_body()?;
let expr_id = db.lookup_expr_by_ptr(function_with_body, stable_ptr).ok()?;
let semantic_db: &dyn SemanticGroup = db;
let ty = semantic_db.expr_semantic(function_with_body, expr_id).ty();
if ty.is_missing(db) {
return None;
}
let module = db.find_module_containing_node(*node)?;
let module_visible_traits = db.visible_traits_from_module(module)?;
let unknown_method_name = node.get_text(db);
Some(
find_methods_for_type(db, resolver, ty, stable_ptr.untyped())
.into_iter()
.filter(|method| method.name(db).to_string(db).as_str() == unknown_method_name)
.filter_map(|method| module_visible_traits.get(&method.trait_id(db)).cloned())
.collect(),
)
}