Split out ModuleKind guessing

This commit is contained in:
oxalica 2023-03-03 21:57:19 +08:00
parent 3487ed42be
commit ba70fc01fd
2 changed files with 79 additions and 62 deletions

View file

@ -0,0 +1,76 @@
use std::collections::HashMap;
use std::sync::Arc;
use smol_str::SmolStr;
use crate::{DefDatabase, FileId, Module};
use super::{BindingValue, Expr, NameId};
/// Guessed kind of a nix file.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ModuleKind {
/// Uncatagorized or ambiguous.
Unknown,
/// Flake definition `flake.nix`.
FlakeNix {
/// Explicit inputs defined in top-level `inputs`.
explicit_inputs: HashMap<SmolStr, NameId>,
/// Implicit inputs introduced in the pat-parameter of `outputs`.
/// NB. `self` parameter is special and is excluded here.
param_inputs: HashMap<SmolStr, NameId>,
},
}
impl ModuleKind {
pub(crate) fn module_kind_query(db: &dyn DefDatabase, file_id: FileId) -> Arc<ModuleKind> {
let module = db.module(file_id);
// Check if it is the flake definition. This is always accurate.
if let Some(flake_info) = db.source_root_flake_info(db.file_source_root(file_id)) {
if flake_info.flake_file == file_id {
return Arc::new(parse_flake_nix(&module));
}
}
Arc::new(ModuleKind::Unknown)
}
}
fn parse_flake_nix(module: &Module) -> ModuleKind {
let mut explicit_inputs = HashMap::new();
let mut param_inputs = HashMap::new();
if let Expr::Attrset(flake_set) = &module[module.entry_expr()] {
for &(name_id, value) in flake_set.statics.iter() {
let BindingValue::Expr(value_expr) = value else { continue };
match &*module[name_id].text {
"inputs" => {
let Expr::Attrset(inputs) = &module[value_expr] else { continue };
explicit_inputs = inputs
.statics
.iter()
.map(|&(input_name_id, _)| {
(module[input_name_id].text.clone(), input_name_id)
})
.collect();
}
"outputs" => {
let Expr::Lambda(_, Some(pat), _) = &module[value_expr] else { continue };
param_inputs = pat
.fields
.iter()
.filter_map(|&(name_id, _)| name_id)
.map(|name_id| (module[name_id].text.clone(), name_id))
// Exclude `self`.
.filter(|(name, _)| name != "self")
.collect();
}
_ => {}
}
}
}
ModuleKind::FlakeNix {
explicit_inputs,
param_inputs,
}
}

View file

@ -1,3 +1,4 @@
mod kind;
mod liveness;
mod lower;
mod nameres;
@ -18,6 +19,7 @@ use std::ops;
use std::sync::Arc;
use syntax::Parse;
pub use self::kind::ModuleKind;
pub use self::liveness::LivenessCheckResult;
pub use self::nameres::{
ModuleScopes, NameReference, NameResolution, ResolveResult, ScopeData, ScopeId,
@ -38,6 +40,7 @@ pub trait DefDatabase: SourceDatabase {
fn source_map(&self, file_id: FileId) -> Arc<ModuleSourceMap>;
#[salsa::invoke(ModuleKind::module_kind_query)]
fn module_kind(&self, file_id: FileId) -> Arc<ModuleKind>;
#[salsa::invoke(Module::module_references_query)]
@ -407,65 +410,3 @@ impl Bindings {
.find_map(|&(name_id, value)| (module[name_id].text == name).then_some(value))
}
}
/// Guessed kind of a nix file.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ModuleKind {
/// Uncatagorized or ambiguous.
Unknown,
/// Flake definition `flake.nix`.
FlakeNix {
/// Explicit inputs defined in top-level `inputs`.
explicit_inputs: HashMap<SmolStr, NameId>,
/// Implicit inputs introduced in the pat-parameter of `outputs`.
/// NB. `self` parameter is special and is excluded here.
param_inputs: HashMap<SmolStr, NameId>,
},
}
fn module_kind(db: &dyn DefDatabase, file_id: FileId) -> Arc<ModuleKind> {
let module = db.module(file_id);
// Check if it is the flake definition. This is always accurate.
if let Some(flake_info) = db.source_root_flake_info(db.file_source_root(file_id)) {
if flake_info.flake_file == file_id {
let mut explicit_inputs = HashMap::new();
let mut param_inputs = HashMap::new();
if let Expr::Attrset(flake_set) = &module[module.entry_expr()] {
for &(name_id, value) in flake_set.statics.iter() {
let BindingValue::Expr(value_expr) = value else { continue };
match &*module[name_id].text {
"inputs" => {
let Expr::Attrset(inputs) = &module[value_expr] else { continue };
explicit_inputs = inputs
.statics
.iter()
.map(|&(input_name_id, _)| {
(module[input_name_id].text.clone(), input_name_id)
})
.collect();
}
"outputs" => {
let Expr::Lambda(_, Some(pat), _) = &module[value_expr] else { continue };
param_inputs = pat
.fields
.iter()
.filter_map(|&(name_id, _)| name_id)
.map(|name_id| (module[name_id].text.clone(), name_id))
// Exclude `self`.
.filter(|(name, _)| name != "self")
.collect();
}
_ => {}
}
}
}
return Arc::new(ModuleKind::FlakeNix {
explicit_inputs,
param_inputs,
});
}
}
Arc::new(ModuleKind::Unknown)
}