use std::collections::BTreeMap;
use std::path::PathBuf;
pub mod database;
pub mod diagnostics;
pub mod gradual;
pub mod incremental;
pub mod inputs;
pub mod metrics;
pub mod on_demand;
pub mod queries;
pub use inputs::{FileDigest, PackageManifest, SourceFile, TextChange};
pub use queries::{
check_implements, completions_at, file_symbols, infer_function_type, parse_file,
resolve_symbol_at, type_check_file, BinaryOp, CompletionItem, CompletionKind, Expr, Function,
FunctionSignature, Import, InferredType, Interface, OrderedFloat, ParseError, ParsedFile,
Statement, SymbolIndex, TypeCheckResult,
};
pub use database::TypeDatabase;
pub use diagnostics::{
render_diagnostic, type_error_to_diagnostic, Color, FileCache, RichDiagnostic, Severity,
};
pub use gradual::{GradualChecker, GradualMode, PythonInterop, RuntimeTag};
pub use incremental::{ChangeSet, ChangeType, DependencyGraph, IncrementalProcessor};
pub use metrics::{MetricsCollector, MetricsSnapshot, PerformanceBudget, Timer};
pub use on_demand::{IndexStats, PackageLoader, WorkspaceIndex};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Type {
Int,
Float,
String,
Bool,
Any,
Unit,
Array(Box<Type>),
Map(Box<Type>, Box<Type>),
Func(Vec<Type>, Box<Type>),
Option(Box<Type>),
Result(Box<Type>, Box<Type>),
Named(String),
Struct(BTreeMap<String, Type>),
Tuple(Vec<Type>),
Tensor,
Unknown,
}
impl std::fmt::Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Int => write!(f, "int"),
Type::Float => write!(f, "float"),
Type::String => write!(f, "string"),
Type::Bool => write!(f, "bool"),
Type::Any => write!(f, "any"),
Type::Unit => write!(f, "()"),
Type::Array(t) => write!(f, "[]{}", t),
Type::Map(k, v) => write!(f, "map[{}, {}]", k, v),
Type::Func(args, ret) => {
write!(f, "fn(")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", arg)?;
}
write!(f, ") -> {}", ret)
}
Type::Option(t) => write!(f, "?{}", t),
Type::Result(ok, err) => write!(f, "result[{}, {}]", ok, err),
Type::Named(n) => write!(f, "{}", n),
Type::Struct(_) => write!(f, "struct"),
Type::Tuple(elems) => {
write!(f, "(")?;
for (i, e) in elems.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", e)?;
}
write!(f, ")")
}
Type::Tensor => write!(f, "Tensor"),
Type::Unknown => write!(f, "unknown"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Location {
pub file: PathBuf,
pub span: Span,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct Span {
pub start: usize,
pub end: usize,
pub line: usize,
pub column: usize,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Symbol {
pub name: String,
pub kind: SymbolKind,
pub ty: Type,
pub location: Location,
pub is_exported: bool,
pub docs: Option<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SymbolKind {
Function,
Variable,
Type,
Interface,
Constant,
Module,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct TypeError {
pub message: String,
pub span: Span,
pub error_type: ErrorType,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum ErrorType {
TypeMismatch { expected: Type, found: Type },
UnknownIdentifier(String),
UnknownField { ty: Type, field: String },
WrongArity { expected: usize, found: usize },
NotCallable(Type),
InvalidOperation { op: String, ty: Type },
MissingReturn,
UnreachableCode,
Generic(String),
}
pub fn create_database() -> TypeDatabase {
TypeDatabase::new()
}
pub fn create_gradual_checker(mode: GradualMode) -> GradualChecker {
GradualChecker::new(mode)
}
pub fn create_metrics() -> MetricsCollector {
MetricsCollector::new()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_type_display() {
assert_eq!(Type::Int.to_string(), "int");
assert_eq!(Type::Array(Box::new(Type::Int)).to_string(), "[]int");
assert_eq!(
Type::Func(vec![Type::Int, Type::Int], Box::new(Type::Bool)).to_string(),
"fn(int, int) -> bool"
);
}
#[test]
fn test_create_database() {
let db = create_database();
let _ = db.metrics();
}
#[test]
fn test_end_to_end() {
use salsa::Setter;
let mut db = create_database();
let source = SourceFile::new(
&db,
PathBuf::from("test.go"),
"func main() {}\nfunc Add(a int, b int) int { return a + b }".to_string(),
1,
);
let parsed = parse_file(&db, source);
assert_eq!(parsed.functions(&db).len(), 2);
let symbols = file_symbols(&db, source);
assert_eq!(symbols.exports(&db).len(), 1);
let result = type_check_file(&db, source);
assert!(result.success(&db));
source.set_content(&mut db).to("func main() {}".to_string());
source.set_version(&mut db).to(2);
let parsed2 = parse_file(&db, source);
assert_eq!(parsed2.functions(&db).len(), 1);
}
}