use std::sync::Arc;
use mir_issues::Issue;
use mir_types::Type;
use super::*;
#[derive(Clone, Debug)]
pub struct FunctionInferenceResult {
pub issues: Vec<Issue>,
pub ref_locs: Vec<RefLoc>,
pub return_type: Option<Type>,
}
impl PartialEq for FunctionInferenceResult {
fn eq(&self, other: &Self) -> bool {
self.issues == other.issues
&& self.ref_locs == other.ref_locs
&& self.return_type == other.return_type
}
}
impl Eq for FunctionInferenceResult {}
fn find_function_decl<'a>(
program: &'a php_ast::owned::Program,
db: &dyn MirDatabase,
file: &str,
target_fqn: &str,
) -> Option<&'a php_ast::owned::FunctionDecl> {
use php_ast::owned::StmtKind;
let mut found: Option<&'a php_ast::owned::FunctionDecl> = None;
crate::body_analysis::for_each_file_scope_decl(&program.stmts, &mut |stmt| {
if found.is_some() {
return;
}
if let StmtKind::Function(decl) = &stmt.kind {
let name = decl.name.as_deref().unwrap_or("");
if !name.is_empty() && crate::db::resolve_name(db, file, name) == target_fqn {
found = Some(decl);
}
}
});
found
}
#[salsa::tracked(lru = 4096)]
pub fn infer_function(
db: &dyn MirDatabase,
file: SourceFile,
fn_fqn: Arc<str>,
) -> Option<Arc<FunctionInferenceResult>> {
let prepared = crate::db::queries::prepare_analysis_file(db, file);
let parsed = prepared.parse_result();
if prepared.has_hard_parse_errors {
return None;
}
let decl = find_function_decl(&parsed.program, db, prepared.path.as_ref(), fn_fqn.as_ref())?;
let driver = crate::body_analysis::BodyAnalyzer::new(db, prepared.php_version);
let result = driver.analyze_fn_decl_pure(
decl,
prepared.path,
prepared.text.as_ref(),
&parsed.source_map,
);
crate::metrics::record_infer_function_retained(
result.issues.len(),
result.ref_locs.len(),
result.return_type.is_some(),
);
Some(Arc::new(result))
}