Skip to content

Commit

Permalink
Part 1 of testing
Browse files Browse the repository at this point in the history
  • Loading branch information
Rigidity committed Apr 18, 2024
1 parent bea2c95 commit e8e6625
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 85 deletions.
68 changes: 34 additions & 34 deletions crates/rue-compiler/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ pub enum DiagnosticKind {

#[derive(Debug, Error, Clone, PartialEq, Eq, Hash)]
pub enum WarningKind {
#[error("marking optional types as optional again has no effect")]
UselessOptional,

#[error("redundant type check against `{0}`, value is already that type")]
RedundantTypeGuard(String),

#[error("unused function `{0}`")]
UnusedFunction(String),

Expand All @@ -56,28 +50,40 @@ pub enum WarningKind {
#[error("unused let binding `{0}`")]
UnusedLet(String),

#[error("unused type `{0}`")]
UnusedType(String),
#[error("unused enum `{0}`")]
UnusedEnum(String),

#[error("unused enum variant `{0}`")]
UnusedEnumVariant(String),

#[error("unused struct `{0}`")]
UnusedStruct(String),

#[error("unused type alias `{0}`")]
UnusedTypeAlias(String),

#[error("marking optional types as optional again has no effect")]
UselessOptionalType,

#[error("redundant type check against `{0}`, value is already that type")]
RedundantTypeCheck(String),
}

#[derive(Debug, Error, Clone, PartialEq, Eq, Hash)]
pub enum ErrorKind {
#[error("missing `main` function")]
MissingMain,

#[error("undefined reference `{0}`")]
#[error("cannot reference undefined symbol `{0}`")]
UndefinedReference(String),

#[error("undefined type `{0}`")]
#[error("cannot reference undefined type `{0}`")]
UndefinedType(String),

#[error("recursive type alias")]
#[error("type aliases cannot reference themselves recursively")]
RecursiveTypeAlias,

#[error("expected {expected} arguments, found {found}")]
ArgumentMismatch { expected: usize, found: usize },

#[error("expected type `{expected}`, found `{found}`")]
#[error("expected type `{expected}`, but found `{found}`")]
TypeMismatch { expected: String, found: String },

#[error("cannot cast type `{found}` to `{expected}`")]
Expand All @@ -86,33 +92,27 @@ pub enum ErrorKind {
#[error("cannot call expression with type `{0}`")]
UncallableType(String),

#[error("expected {expected} arguments, but found {found}")]
ArgumentMismatch { expected: usize, found: usize },

#[error("uninitializable type `{0}`")]
UninitializableType(String),

#[error("duplicate field `{0}`")]
DuplicateField(String),

#[error("undefined field `{0}`")]
UndefinedField(String),
#[error("undefined field `{field}` on type `{ty}`")]
UndefinedField { field: String, ty: String },

#[error("missing fields: {}", join_names(.0))]
MissingFields(Vec<String>),
#[error("missing fields on type `{ty}`: {}", join_names(.fields))]
MissingFields { fields: Vec<String>, ty: String },

#[error("cannot access named field of non-struct type `{0}`")]
NonStructFieldAccess(String),
#[error("cannot access field `{field}` of non-struct type `{ty}`")]
InvalidFieldAccess { field: String, ty: String },

#[error("unknown field of pair type `{0}`, expected `first` or `rest`")]
PairFieldAccess(String),

#[error("unknown field of bytes type `{0}`, expected `length`")]
BytesFieldAccess(String),

#[error("cannot index non-list type `{0}`")]
#[error("cannot index into non-list type `{0}`")]
IndexAccess(String),

#[error("index `{0}` out of bounds, length is `{1}`")]
IndexOutOfBounds(u32, u32),

#[error("the spread operator can only be used on the last element")]
NonFinalSpread,

Expand All @@ -125,15 +125,15 @@ pub enum ErrorKind {
#[error("duplicate enum variant `{0}`")]
DuplicateEnumVariant(String),

#[error("unknown enum variant `{0}`")]
UnknownEnumVariant(String),

#[error("paths are not allowed in this context")]
PathNotAllowed,

#[error("cannot path into non-enum type `{0}`")]
PathIntoNonEnum(String),

#[error("unknown enum variant `{0}`")]
UnknownEnumVariant(String),

#[error("cannot check type `{from}` against `{to}`")]
UnsupportedTypeGuard { from: String, to: String },

Expand Down
77 changes: 45 additions & 32 deletions crates/rue-compiler/src/lowerer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,7 @@ impl<'a> Lowerer<'a> {
match ty.map(|ty| self.db.ty(ty)).cloned() {
Some(Type::Struct(struct_type)) => {
let hir_id = self.compile_initializer_fields(
ty.unwrap(),
struct_type.fields(),
initializer.fields(),
initializer.syntax().text_range(),
Expand All @@ -768,6 +769,7 @@ impl<'a> Lowerer<'a> {
}
Some(Type::EnumVariant(enum_variant)) => {
let fields_hir_id = self.compile_initializer_fields(
ty.unwrap(),
enum_variant.fields(),
initializer.fields(),
initializer.syntax().text_range(),
Expand Down Expand Up @@ -796,6 +798,7 @@ impl<'a> Lowerer<'a> {
/// Compiles the fields of an initializer into a list.
fn compile_initializer_fields(
&mut self,
struct_type: TypeId,
struct_fields: &IndexMap<String, TypeId>,
initializer_fields: Vec<InitializerField>,
text_range: TextRange,
Expand Down Expand Up @@ -831,7 +834,10 @@ impl<'a> Lowerer<'a> {
);
} else if !struct_fields.contains_key(name.text()) {
self.error(
ErrorKind::UndefinedField(name.to_string()),
ErrorKind::UndefinedField {
field: name.to_string(),
ty: self.type_name(struct_type),
},
name.text_range(),
);
} else {
Expand All @@ -847,7 +853,13 @@ impl<'a> Lowerer<'a> {
.collect();

if !missing_fields.is_empty() {
self.error(ErrorKind::MissingFields(missing_fields), text_range);
self.error(
ErrorKind::MissingFields {
fields: missing_fields,
ty: self.type_name(struct_type),
},
text_range,
);
}

let mut hir_id = self.nil_hir;
Expand Down Expand Up @@ -882,46 +894,44 @@ impl<'a> Lowerer<'a> {
Type::Struct(struct_type) => {
if let Some(field) = struct_type.fields().get_full(field_name.text()) {
let (index, _, field_type) = field;
Value::typed(self.compile_index(value.hir(), index, false), *field_type)
return Value::typed(
self.compile_index(value.hir(), index, false),
*field_type,
);
} else {
self.error(
ErrorKind::UndefinedField(field_name.to_string()),
ErrorKind::UndefinedField {
field: field_name.to_string(),
ty: self.type_name(value.ty()),
},
field_name.text_range(),
);
self.unknown()
return self.unknown();
}
}
Type::Pair(left, right) => match field_name.text() {
"first" => Value::typed(self.db.alloc_hir(Hir::First(value.hir())), left),
"rest" => Value::typed(self.db.alloc_hir(Hir::Rest(value.hir())), right),
_ => {
self.error(
ErrorKind::PairFieldAccess(field_name.to_string()),
field_name.text_range(),
);
self.unknown()
"first" => {
return Value::typed(self.db.alloc_hir(Hir::First(value.hir())), left);
}
},
Type::Bytes | Type::Bytes32 => match field_name.text() {
"length" => {
Value::typed(self.db.alloc_hir(Hir::Strlen(value.hir())), self.int_type)
}
_ => {
self.error(
ErrorKind::BytesFieldAccess(field_name.to_string()),
field_name.text_range(),
);
self.unknown()
"rest" => {
return Value::typed(self.db.alloc_hir(Hir::Rest(value.hir())), right);
}
_ => {}
},
_ => {
self.error(
ErrorKind::NonStructFieldAccess(self.type_name(value.ty())),
field_name.text_range(),
);
self.unknown()
Type::Bytes | Type::Bytes32 if field_name.text() == "length" => {
return Value::typed(self.db.alloc_hir(Hir::Strlen(value.hir())), self.int_type);
}
_ => {}
}

self.error(
ErrorKind::InvalidFieldAccess {
field: field_name.to_string(),
ty: self.type_name(value.ty()),
},
field_name.text_range(),
);
self.unknown()
}

fn compile_index_access(&mut self, index_access: IndexAccess) -> Value {
Expand Down Expand Up @@ -1203,7 +1213,7 @@ impl<'a> Lowerer<'a> {
) -> Option<(Guard, HirId)> {
if self.types_equal(from, to) {
self.warning(
WarningKind::RedundantTypeGuard(self.type_name(from)),
WarningKind::RedundantTypeCheck(self.type_name(from)),
text_range,
);
return Some((Guard::new(to, self.bool_type), hir_id));
Expand Down Expand Up @@ -1923,7 +1933,10 @@ impl<'a> Lowerer<'a> {
.unwrap_or(self.unknown_type);

if let Type::Optional(inner) = self.db.ty_raw(ty).clone() {
self.warning(WarningKind::UselessOptional, optional.syntax().text_range());
self.warning(
WarningKind::UselessOptionalType,
optional.syntax().text_range(),
);
return inner;
}

Expand Down
50 changes: 36 additions & 14 deletions crates/rue-compiler/src/optimizer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;

use indexmap::IndexSet;
use rowan::TextRange;
Expand All @@ -8,6 +8,7 @@ use crate::{
hir::{BinOp, Hir},
lir::Lir,
symbol::Symbol,
ty::Type,
Diagnostic, DiagnosticKind, TypeId, WarningKind,
};

Expand All @@ -16,8 +17,8 @@ struct Environment {
definitions: IndexSet<SymbolId>,
captures: IndexSet<SymbolId>,
parameters: IndexSet<SymbolId>,
used_symbols: HashSet<SymbolId>,
used_types: HashSet<TypeId>,
used_symbols: IndexSet<SymbolId>,
used_types: IndexSet<TypeId>,
varargs: bool,
inherits_from: Option<ScopeId>,
}
Expand Down Expand Up @@ -94,8 +95,8 @@ impl<'a> Optimizer<'a> {
fn check_unused(
&mut self,
scope_id: ScopeId,
used_symbols: HashSet<SymbolId>,
used_types: HashSet<TypeId>,
used_symbols: IndexSet<SymbolId>,
used_types: IndexSet<TypeId>,
) {
let unused_symbols: Vec<SymbolId> = self
.db
Expand All @@ -109,11 +110,19 @@ impl<'a> Optimizer<'a> {
for symbol_id in unused_symbols {
if let Some(token) = self.db.symbol_token(symbol_id) {
match self.db.symbol(symbol_id).clone() {
Symbol::Function { .. } => {
Symbol::Function {
scope_id: function_scope_id,
hir_id,
..
} => {
self.warning(
WarningKind::UnusedFunction(token.to_string()),
token.text_range(),
);

// Even though it's unused, we should check for captures.
// This is so we can warn about unused captures within the function scope.
self.compute_captures_entrypoint(function_scope_id, None, hir_id);
}
Symbol::Parameter { .. } => {
self.warning(
Expand Down Expand Up @@ -148,10 +157,25 @@ impl<'a> Optimizer<'a> {

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(),
);
match self.db.ty_raw(type_id) {
Type::Alias(..) => self.warning(
WarningKind::UnusedTypeAlias(token.to_string()),
token.text_range(),
),
Type::Enum(..) => self.warning(
WarningKind::UnusedEnum(token.to_string()),
token.text_range(),
),
Type::EnumVariant(..) => self.warning(
WarningKind::UnusedEnumVariant(token.to_string()),
token.text_range(),
),
Type::Struct(..) => self.warning(
WarningKind::UnusedStruct(token.to_string()),
token.text_range(),
),
_ => {}
}
}
}
}
Expand Down Expand Up @@ -293,10 +317,8 @@ impl<'a> Optimizer<'a> {

let mut args = Vec::new();

for symbol_id in self.db.scope(scope_id).local_symbols() {
if self.db.symbol(symbol_id).is_definition() {
args.push(self.opt_definition(scope_id, symbol_id));
}
for symbol_id in self.env(scope_id).definitions.clone() {
args.push(self.opt_definition(scope_id, symbol_id));
}

for symbol_id in self.env(scope_id).captures.clone() {
Expand Down
Loading

0 comments on commit e8e6625

Please sign in to comment.