use super::prelude::*;
pub(super) const SINK_MODULES: &[(&str, Capability)] = &[
("net", Capability::Network),
("process", Capability::ProcessExec),
];
pub(super) fn classify_function_as_sink(
func: ra_ap_hir::Function,
db: &RootDatabase,
) -> Option<Capability> {
match_sink_in_module(func.module(db), db)
}
pub(super) fn match_sink_in_module(
module: ra_ap_hir::Module,
db: &RootDatabase,
) -> Option<Capability> {
let mut current = Some(module);
while let Some(m) = current {
let cap = m
.name(db)
.and_then(|name| classify_segment_as_sink(name.as_str()));
match cap {
Some(_) => return cap,
None => current = m.parent(db),
}
}
None
}
pub(super) fn classify_segment_as_sink(name: &str) -> Option<Capability> {
SINK_MODULES
.iter()
.find_map(|(segment, cap)| (*segment == name).then_some(*cap))
}
pub(super) fn classify_qualified_call_by_type(
sema: &Semantics<'_, RootDatabase>,
call: &ast::CallExpr,
db: &RootDatabase,
) -> Option<Capability> {
let expr = call.expr()?;
let path_expr = ast::PathExpr::cast(expr.syntax().clone())?;
let path = path_expr.path()?;
let qualifier = path.qualifier()?;
let resolution = sema.resolve_path(&qualifier)?;
match resolution {
ra_ap_hir::PathResolution::Def(module_def) => {
let module = module_def_module(module_def, db)?;
match_sink_in_module(module, db)
}
_ => None,
}
}
pub(super) fn module_def_module(
def: ra_ap_hir::ModuleDef,
db: &RootDatabase,
) -> Option<ra_ap_hir::Module> {
match def {
ra_ap_hir::ModuleDef::Adt(adt) => Some(adt.module(db)),
ra_ap_hir::ModuleDef::Function(f) => Some(f.module(db)),
ra_ap_hir::ModuleDef::Module(m) => Some(m),
ra_ap_hir::ModuleDef::TypeAlias(ta) => Some(ta.module(db)),
ra_ap_hir::ModuleDef::Trait(t) => Some(t.module(db)),
_ => None,
}
}