From 3db6bc8b2019b56da2bbe95b7707ac08672e8189 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Wed, 17 Apr 2024 16:15:54 -0400 Subject: [PATCH] Unused type warnings, though not indirect --- crates/rue-compiler/src/database.rs | 15 ++++++- crates/rue-compiler/src/error.rs | 3 ++ crates/rue-compiler/src/lowerer.rs | 65 ++++++++++++++++------------ crates/rue-compiler/src/optimizer.rs | 48 +++++++++++++++++++- crates/rue-compiler/src/scope.rs | 15 ++++++- 5 files changed, 115 insertions(+), 31 deletions(-) diff --git a/crates/rue-compiler/src/database.rs b/crates/rue-compiler/src/database.rs index 1338ba1..e066fac 100644 --- a/crates/rue-compiler/src/database.rs +++ b/crates/rue-compiler/src/database.rs @@ -28,6 +28,7 @@ pub struct Database { hir: Arena, lir: Arena, symbol_tokens: HashMap, + type_tokens: HashMap, } impl Database { @@ -45,8 +46,14 @@ impl Database { id } - pub(crate) fn alloc_type(&mut self, ty: Type) -> TypeId { - TypeId(self.types.alloc(ty)) + pub(crate) fn alloc_type(&mut self, ty: Type, token: Option) -> TypeId { + let id = TypeId(self.types.alloc(ty)); + + if let Some(token) = token { + self.type_tokens.insert(id, token); + } + + id } pub(crate) fn alloc_hir(&mut self, hir: Hir) -> HirId { @@ -80,6 +87,10 @@ impl Database { self.ty_raw(id) } + pub fn type_token(&self, id: TypeId) -> Option { + self.type_tokens.get(&id).cloned() + } + pub(crate) fn ty_mut(&mut self, id: TypeId) -> &mut Type { &mut self.types[id.0] } diff --git a/crates/rue-compiler/src/error.rs b/crates/rue-compiler/src/error.rs index 4b09a02..1cd3943 100644 --- a/crates/rue-compiler/src/error.rs +++ b/crates/rue-compiler/src/error.rs @@ -46,6 +46,9 @@ pub enum WarningKind { #[error("unused symbol `{0}`")] UnusedSymbol(String), + + #[error("unused type `{0}`")] + UnusedType(String), } #[derive(Debug, Error, Clone, PartialEq, Eq, Hash)] diff --git a/crates/rue-compiler/src/lowerer.rs b/crates/rue-compiler/src/lowerer.rs index 9c368d7..a8ce557 100644 --- a/crates/rue-compiler/src/lowerer.rs +++ b/crates/rue-compiler/src/lowerer.rs @@ -45,15 +45,15 @@ pub struct Lowerer<'a> { impl<'a> Lowerer<'a> { pub fn new(db: &'a mut Database) -> Self { - let int_type = db.alloc_type(Type::Int); - let bool_type = db.alloc_type(Type::Bool); - let bytes_type = db.alloc_type(Type::Bytes); - let bytes32_type = db.alloc_type(Type::Bytes32); - let pk_type = db.alloc_type(Type::PublicKey); - let any_type = db.alloc_type(Type::Any); - let nil_type = db.alloc_type(Type::Nil); + let int_type = db.alloc_type(Type::Int, None); + let bool_type = db.alloc_type(Type::Bool, None); + let bytes_type = db.alloc_type(Type::Bytes, None); + let bytes32_type = db.alloc_type(Type::Bytes32, None); + let pk_type = db.alloc_type(Type::PublicKey, None); + let any_type = db.alloc_type(Type::Any, None); + let nil_type = db.alloc_type(Type::Nil, None); let nil_hir = db.alloc_hir(Hir::Atom(Vec::new())); - let unknown_type = db.alloc_type(Type::Unknown); + let unknown_type = db.alloc_type(Type::Unknown, None); let unknown_hir = db.alloc_hir(Hir::Unknown); // This is the root scope, with builtins for various types and functions. @@ -288,7 +288,7 @@ impl<'a> Lowerer<'a> { /// Define a type for an alias in the current scope, but leave it as unknown for now. fn declare_type_alias(&mut self, type_alias: TypeAliasItem) -> TypeId { - let type_id = self.db.alloc_type(Type::Unknown); + let type_id = self.db.alloc_type(Type::Unknown, type_alias.name()); if let Some(name) = type_alias.name() { self.scope_mut().define_type(name.to_string(), type_id); } @@ -297,7 +297,7 @@ impl<'a> Lowerer<'a> { /// Define a type for a struct in the current scope, but leave it as unknown for now. fn declare_struct(&mut self, struct_item: StructItem) -> TypeId { - let type_id = self.db.alloc_type(Type::Unknown); + let type_id = self.db.alloc_type(Type::Unknown, struct_item.name()); if let Some(name) = struct_item.name() { self.scope_mut().define_type(name.to_string(), type_id); } @@ -319,10 +319,15 @@ impl<'a> Lowerer<'a> { continue; } - variants.insert(name.to_string(), self.db.alloc_type(Type::Unknown)); + variants.insert( + name.to_string(), + self.db.alloc_type(Type::Unknown, variant.name()), + ); } - let type_id = self.db.alloc_type(Type::Enum(EnumType::new(variants))); + let type_id = self + .db + .alloc_type(Type::Enum(EnumType::new(variants)), enum_item.name()); if let Some(name) = enum_item.name() { self.scope_mut().define_type(name.to_string(), type_id); @@ -1218,7 +1223,9 @@ impl<'a> Lowerer<'a> { Some((Guard::new(to, self.bytes_type), hir_id)) } (Type::Any, Type::Bytes) => { - let pair_type = self.db.alloc_type(Type::Pair(self.any_type, self.any_type)); + let pair_type = self + .db + .alloc_type(Type::Pair(self.any_type, self.any_type), None); let is_cons = self.db.alloc_hir(Hir::IsCons(hir_id)); let hir_id = self.db.alloc_hir(Hir::Not(is_cons)); Some((Guard::new(to, pair_type), hir_id)) @@ -1236,7 +1243,7 @@ impl<'a> Lowerer<'a> { Some((Guard::new(to, self.nil_type), hir_id)) } (Type::List(inner), Type::Nil) => { - let pair_type = self.db.alloc_type(Type::Pair(inner, from)); + let pair_type = self.db.alloc_type(Type::Pair(inner, from), None); let is_cons = self.db.alloc_hir(Hir::IsCons(hir_id)); let hir_id = self.db.alloc_hir(Hir::Not(is_cons)); Some((Guard::new(to, pair_type), hir_id)) @@ -1333,7 +1340,7 @@ impl<'a> Lowerer<'a> { _ => None, }; } else { - list_type = Some(self.db.alloc_type(Type::List(output.ty()))); + list_type = Some(self.db.alloc_type(Type::List(output.ty()), None)); item_type = Some(output.ty()); } } @@ -1361,7 +1368,7 @@ impl<'a> Lowerer<'a> { Value::typed( hir_id, - list_type.unwrap_or_else(|| self.db.alloc_type(Type::List(self.unknown_type))), + list_type.unwrap_or_else(|| self.db.alloc_type(Type::List(self.unknown_type), None)), ) } @@ -1401,7 +1408,7 @@ impl<'a> Lowerer<'a> { }; let hir_id = self.db.alloc_hir(Hir::Pair(first.hir(), rest.hir())); - let type_id = self.db.alloc_type(Type::Pair(first.ty(), rest.ty())); + let type_id = self.db.alloc_type(Type::Pair(first.ty(), rest.ty()), None); Value::typed(hir_id, type_id) } @@ -1485,7 +1492,7 @@ impl<'a> Lowerer<'a> { Value::typed( self.db.alloc_hir(Hir::Reference(symbol_id)), - self.db.alloc_type(Type::Function(ty)), + self.db.alloc_type(Type::Function(ty), None), ) } @@ -1623,7 +1630,9 @@ impl<'a> Lowerer<'a> { self.db.alloc_hir(Hir::Reference(symbol_id)), self.symbol_type(symbol_id) .unwrap_or_else(|| match self.db.symbol(symbol_id) { - Symbol::Function { ty, .. } => self.db.alloc_type(Type::Function(ty.clone())), + Symbol::Function { ty, .. } => { + self.db.alloc_type(Type::Function(ty.clone()), None) + } Symbol::Parameter { type_id } => *type_id, Symbol::LetBinding { type_id, .. } => *type_id, Symbol::ConstBinding { type_id, .. } => *type_id, @@ -1823,8 +1832,11 @@ impl<'a> Lowerer<'a> { return self.unknown_type; }; + self.scope_mut().use_type(ty); + for name in idents { ty = self.path_into_type(ty, name.text(), name.text_range()); + self.scope_mut().use_type(ty); } ty @@ -1852,7 +1864,7 @@ impl<'a> Lowerer<'a> { }; let item_type = self.compile_type(inner); - self.db.alloc_type(Type::List(item_type)) + self.db.alloc_type(Type::List(item_type), None) } fn compile_pair_type(&mut self, pair_type: PairType) -> TypeId { @@ -1866,7 +1878,7 @@ impl<'a> Lowerer<'a> { .map(|ty| self.compile_type(ty)) .unwrap_or(self.unknown_type); - self.db.alloc_type(Type::Pair(first, rest)) + self.db.alloc_type(Type::Pair(first, rest), None) } fn compile_function_type(&mut self, function: AstFunctionType) -> TypeId { @@ -1897,11 +1909,10 @@ impl<'a> Lowerer<'a> { .map(|ty| self.compile_type(ty)) .unwrap_or(self.unknown_type); - self.db.alloc_type(Type::Function(FunctionType::new( - parameter_types, - return_type, - vararg, - ))) + self.db.alloc_type( + Type::Function(FunctionType::new(parameter_types, return_type, vararg)), + None, + ) } fn compile_optional_type(&mut self, optional: OptionalType) -> TypeId { @@ -1918,7 +1929,7 @@ impl<'a> Lowerer<'a> { return inner; } - self.db.alloc_type(Type::Optional(ty)) + self.db.alloc_type(Type::Optional(ty), None) } fn try_unwrap_optional(&mut self, ty: TypeId) -> TypeId { diff --git a/crates/rue-compiler/src/optimizer.rs b/crates/rue-compiler/src/optimizer.rs index 0bc61a4..4c36d4b 100644 --- a/crates/rue-compiler/src/optimizer.rs +++ b/crates/rue-compiler/src/optimizer.rs @@ -8,7 +8,7 @@ use crate::{ hir::{BinOp, Hir}, lir::Lir, symbol::Symbol, - Diagnostic, DiagnosticKind, WarningKind, + Diagnostic, DiagnosticKind, TypeId, WarningKind, }; #[derive(Default)] @@ -17,6 +17,7 @@ struct Environment { captures: IndexSet, parameters: IndexSet, used_symbols: HashSet, + used_types: HashSet, varargs: bool, inherits_from: Option, } @@ -84,6 +85,10 @@ impl<'a> Optimizer<'a> { for symbol_id in self.env(scope_id).used_symbols.clone() { self.env_mut(parent_scope_id).used_symbols.insert(symbol_id); } + + for type_id in self.env(scope_id).used_types.clone() { + self.env_mut(parent_scope_id).used_types.insert(type_id); + } } fn compute_captures_entrypoint( @@ -97,6 +102,8 @@ impl<'a> Optimizer<'a> { } self.environments.insert(scope_id, Environment::default()); + let used_types = self.db.scope(scope_id).used_types().clone(); + self.env_mut(scope_id).used_types.extend(used_types); self.env_mut(scope_id).inherits_from = inherits_from; self.compute_captures_hir(scope_id, hir_id); @@ -117,6 +124,24 @@ impl<'a> Optimizer<'a> { ); } } + + let unused_types: Vec = self + .db + .scope(scope_id) + .local_types() + .iter() + .filter(|&type_id| !self.env(scope_id).used_types.contains(type_id)) + .copied() + .collect(); + + for type_id in unused_types { + if let Some(token) = self.db.type_token(type_id) { + self.warning( + WarningKind::UnusedType(token.to_string()), + token.text_range(), + ); + } + } } fn compute_captures_hir(&mut self, scope_id: ScopeId, hir_id: HirId) { @@ -235,6 +260,27 @@ impl<'a> Optimizer<'a> { } } + let unused_types: Vec = self + .db + .scope(root_scope_id) + .local_types() + .iter() + .filter(|&type_id| { + !self.env(scope_id).used_types.contains(type_id) + && !self.db.scope(root_scope_id).used_types().contains(type_id) + }) + .copied() + .collect(); + + for type_id in unused_types { + if let Some(token) = self.db.type_token(type_id) { + self.warning( + WarningKind::UnusedType(token.to_string()), + token.text_range(), + ); + } + } + for symbol_id in self.db.scope(scope_id).local_symbols() { if self.db.symbol(symbol_id).is_parameter() { self.env_mut(scope_id).parameters.insert(symbol_id); diff --git a/crates/rue-compiler/src/scope.rs b/crates/rue-compiler/src/scope.rs index accbfc5..7bb8c1d 100644 --- a/crates/rue-compiler/src/scope.rs +++ b/crates/rue-compiler/src/scope.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use indexmap::IndexSet; @@ -10,6 +10,7 @@ pub struct Scope { type_aliases: HashMap, type_names: HashMap, local_symbols: IndexSet, + used_types: HashSet, } impl Scope { @@ -42,4 +43,16 @@ impl Scope { pub fn local_symbols(&self) -> Vec { self.local_symbols.iter().copied().collect() } + + pub fn local_types(&self) -> Vec { + self.type_names.keys().copied().collect() + } + + pub fn use_type(&mut self, type_id: TypeId) { + self.used_types.insert(type_id); + } + + pub fn used_types(&self) -> &HashSet { + &self.used_types + } }