use std::collections::BTreeSet;
use lanekeep_core::files::FileAccess;
use lanekeep_core::{AnalysisBudget, FilePath};
use crate::types::{Symbol, Type};
#[derive(Debug, Clone, Copy)]
pub struct Query<'a> {
pub file: &'a FilePath,
pub tree: &'a tree_sitter::Tree,
pub source: &'a str,
pub node: tree_sitter::Node<'a>,
pub files: &'a FileAccess,
}
pub trait TypeProvider: Send + Sync {
fn type_of(&self, q: Query<'_>) -> Option<Type>;
fn symbol_of(&self, q: Query<'_>) -> Option<Symbol>;
fn return_type_of(&self, q: Query<'_>) -> Option<Type>;
fn is_assignable_to(&self, q: Query<'_>, module: &str, name: &str) -> Option<bool>;
fn complete(&self, q: Query<'_>) -> bool;
fn identity(&self) -> Vec<u8>;
fn dependency_paths(&self) -> BTreeSet<FilePath> {
BTreeSet::new()
}
fn revalidate(&self, files: &FileAccess) {
let _ = files;
}
fn begin_run(
&self,
files: &dyn Fn() -> Vec<FilePath>,
budget: AnalysisBudget,
) -> Result<Vec<u8>, BeginRunError> {
let _ = (files, budget);
Ok(Vec::new())
}
fn failure(&self) -> Option<BeginRunError> {
None
}
fn notices(&self) -> Vec<String> {
Vec::new()
}
fn needs_rebuild(&self) -> bool {
false
}
fn spends_analysis_budget(&self) -> bool {
false
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BeginRunError {
Timeout(String),
Failed(String),
}
#[cfg(test)]
mod tests {
use std::sync::atomic::{AtomicUsize, Ordering};
use super::*;
#[derive(Default)]
struct Counting(AtomicUsize);
impl TypeProvider for Counting {
fn type_of(&self, _q: Query<'_>) -> Option<Type> {
None
}
fn symbol_of(&self, _q: Query<'_>) -> Option<Symbol> {
None
}
fn return_type_of(&self, _q: Query<'_>) -> Option<Type> {
None
}
fn is_assignable_to(&self, _q: Query<'_>, _module: &str, _name: &str) -> Option<bool> {
None
}
fn complete(&self, _q: Query<'_>) -> bool {
false
}
fn identity(&self) -> Vec<u8> {
Vec::new()
}
fn revalidate(&self, _files: &FileAccess) {
self.0.fetch_add(1, Ordering::Relaxed);
}
}
#[derive(Default)]
struct Silent;
impl TypeProvider for Silent {
fn type_of(&self, _q: Query<'_>) -> Option<Type> {
None
}
fn symbol_of(&self, _q: Query<'_>) -> Option<Symbol> {
None
}
fn return_type_of(&self, _q: Query<'_>) -> Option<Type> {
None
}
fn is_assignable_to(&self, _q: Query<'_>, _module: &str, _name: &str) -> Option<bool> {
None
}
fn complete(&self, _q: Query<'_>) -> bool {
false
}
fn identity(&self) -> Vec<u8> {
Vec::new()
}
}
#[test]
fn revalidate_reaches_an_implementor_through_the_trait_object() {
let counting = std::sync::Arc::new(Counting::default());
let held: std::sync::Arc<dyn TypeProvider> = counting.clone();
let files = FileAccess::new(std::path::Path::new("."));
held.revalidate(&files);
held.revalidate(&files);
assert_eq!(
counting.0.load(Ordering::Relaxed),
2,
"a session revalidates once per request, through the trait object it holds"
);
}
#[test]
fn a_provider_that_holds_nothing_need_not_override_revalidate() {
let held: std::sync::Arc<dyn TypeProvider> = std::sync::Arc::new(Silent);
held.revalidate(&FileAccess::new(std::path::Path::new(".")));
}
}