Skip to content

Commit

Permalink
Test runner
Browse files Browse the repository at this point in the history
  • Loading branch information
Grant Wuerker committed Mar 6, 2023
1 parent 5eb4e2d commit c4e9579
Show file tree
Hide file tree
Showing 653 changed files with 1,677 additions and 977 deletions.
599 changes: 521 additions & 78 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions crates/analyzer/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::namespace::items::{
self, ContractFieldId, ContractId, DepGraphWrapper, EnumVariantKind, FunctionId, FunctionSigId,
ImplId, IngotId, Item, ModuleConstantId, ModuleId, StructFieldId, StructId, TraitId,
TypeAliasId,
self, AttributeId, ContractFieldId, ContractId, DepGraphWrapper, EnumVariantKind, FunctionId,
FunctionSigId, ImplId, IngotId, Item, ModuleConstantId, ModuleId, StructFieldId, StructId,
TraitId, TypeAliasId,
};
use crate::namespace::types::{self, Type, TypeId};
use crate::{
Expand Down Expand Up @@ -35,6 +35,8 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
#[salsa::interned]
fn intern_enum(&self, data: Rc<items::Enum>) -> EnumId;
#[salsa::interned]
fn intern_attribute(&self, data: Rc<items::Attribute>) -> AttributeId;
#[salsa::interned]
fn intern_enum_variant(&self, data: Rc<items::EnumVariant>) -> EnumVariantId;
#[salsa::interned]
fn intern_trait(&self, data: Rc<items::Trait>) -> TraitId;
Expand Down Expand Up @@ -106,6 +108,8 @@ pub trait AnalyzerDb: SourceDb + Upcast<dyn SourceDb> + UpcastMut<dyn SourceDb>
fn module_parent_module(&self, module: ModuleId) -> Option<ModuleId>;
#[salsa::invoke(queries::module::module_submodules)]
fn module_submodules(&self, module: ModuleId) -> Rc<[ModuleId]>;
#[salsa::invoke(queries::module::module_tests)]
fn module_tests(&self, module: ModuleId) -> Vec<FunctionId>;

// Module Constant
#[salsa::cycle(queries::module::module_constant_type_cycle)]
Expand Down
5 changes: 3 additions & 2 deletions crates/analyzer/src/db/queries/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ pub fn function_signature(
let mut names = HashMap::new();
let mut labels = HashMap::new();

function.data(db).ast.kind.generic_params.kind.iter().fold(
let sig_ast = &function.data(db).ast.kind;
sig_ast.generic_params.kind.iter().fold(
HashMap::<SmolStr, Node<_>>::new(),
|mut accum, param| {
if let Some(previous) = accum.get(&param.name()) {
Expand All @@ -56,7 +57,7 @@ pub fn function_signature(
"generic function parameters aren't yet supported outside of struct functions",
vec![Label::primary(
function.data(db).ast.kind.generic_params.span,
"This can not appear here",
"this cannot appear here",
)],
vec!["Hint: Struct functions can have generic parameters".into()],
);
Expand Down
50 changes: 48 additions & 2 deletions crates/analyzer/src/db/queries/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use crate::context::{Analysis, AnalyzerContext, Constant, NamedThing};
use crate::display::Displayable;
use crate::errors::{self, ConstEvalError, TypeError};
use crate::namespace::items::{
Contract, ContractId, Enum, Function, Impl, ImplId, Item, ModuleConstant, ModuleConstantId,
ModuleId, ModuleSource, Struct, StructId, Trait, TraitId, TypeAlias, TypeDef,
Attribute, Contract, ContractId, Enum, Function, FunctionId, Impl, ImplId, Item,
ModuleConstant, ModuleConstantId, ModuleId, ModuleSource, Struct, StructId, Trait, TraitId,
TypeAlias, TypeDef,
};
use crate::namespace::scopes::ItemScope;
use crate::namespace::types::{self, TypeId};
Expand Down Expand Up @@ -100,6 +101,12 @@ pub fn module_all_items(db: &dyn AnalyzerDb, module: ModuleId) -> Rc<[Item]> {
ast: node.clone(),
module,
})))),
ast::ModuleStmt::Attribute(node) => {
Some(Item::Attribute(db.intern_attribute(Rc::new(Attribute {
ast: node.clone(),
module,
}))))
}
ast::ModuleStmt::Pragma(_) | ast::ModuleStmt::Use(_) | ast::ModuleStmt::Impl(_) => None,
ast::ModuleStmt::ParseError(_) => None,
})
Expand Down Expand Up @@ -161,6 +168,36 @@ pub fn module_item_map(
let mut map = IndexMap::<SmolStr, Item>::new();

for item in module.all_items(db).iter() {
if matches!(item, Item::Attribute(_)) {
continue;
}

if let Item::Function(function) = item {
let sig_ast = &function.data(db).ast.kind.sig.kind;
if function.is_test(db) {
if !sig_ast.generic_params.kind.is_empty() {
diagnostics.push(errors::fancy_error(
"generic function parameters are not supported on test functions",
vec![Label::primary(
sig_ast.generic_params.span,
"this cannot appear here",
)],
vec!["Hint: remove the generic parameters".into()],
));
}

if !sig_ast.args.is_empty() {
let span =
sig_ast.args.first().unwrap().span + sig_ast.args.last().unwrap().span;
diagnostics.push(errors::fancy_error(
"function parameters are not supported on test functions",
vec![Label::primary(span, "this cannot appear here")],
vec!["Hint: remove the parameters".into()],
));
}
}
}

let item_name = item.name(db);
if let Some(global_item) = global_items.get(&item_name) {
let kind = item.item_kind_display_name();
Expand Down Expand Up @@ -710,3 +747,12 @@ fn resolve_use_tree(
}
}
}

pub fn module_tests(db: &dyn AnalyzerDb, ingot: ModuleId) -> Vec<FunctionId> {
ingot
.all_functions(db)
.iter()
.copied()
.filter(|function| function.is_test(db))
.collect()
}
86 changes: 84 additions & 2 deletions crates/analyzer/src/namespace/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub enum Item {
// We can't represent keccak256's arg type yet.
BuiltinFunction(builtins::GlobalFunction),
Intrinsic(builtins::Intrinsic),
Attribute(AttributeId),
}

impl Item {
Expand All @@ -56,6 +57,7 @@ impl Item {
Item::Constant(id) => id.name(db),
Item::Ingot(id) => id.name(db),
Item::Module(id) => id.name(db),
Item::Attribute(id) => id.name(db),
}
}

Expand All @@ -70,7 +72,8 @@ impl Item {
| Item::Intrinsic(_)
| Item::Ingot(_)
| Item::Module(_)
| Item::Impl(_) => None,
| Item::Impl(_)
| Item::Attribute(_) => None,
}
}

Expand All @@ -83,6 +86,7 @@ impl Item {
| Self::Intrinsic(_)
| Self::Impl(_)
| Self::GenericType(_) => true,
Self::Attribute(_) => false,
Self::Type(id) => id.is_public(db),
Self::Trait(id) => id.is_public(db),
Self::Function(id) => id.is_public(db),
Expand All @@ -102,6 +106,7 @@ impl Item {
| Item::Function(_)
| Item::Constant(_)
| Item::Ingot(_)
| Item::Attribute(_)
| Item::Module(_) => false,
}
}
Expand All @@ -125,6 +130,7 @@ impl Item {
Item::Constant(_) => "constant",
Item::Ingot(_) => "ingot",
Item::Module(_) => "module",
Item::Attribute(_) => "attribute",
}
}

Expand All @@ -139,6 +145,7 @@ impl Item {
| Item::Function(_)
| Item::Constant(_)
| Item::BuiltinFunction(_)
| Item::Attribute(_)
| Item::Intrinsic(_) => Rc::new(indexmap! {}),
}
}
Expand All @@ -152,6 +159,7 @@ impl Item {
Item::Function(id) => Some(id.parent(db)),
Item::Constant(id) => Some(id.parent(db)),
Item::Module(id) => Some(id.parent(db)),
Item::Attribute(id) => Some(id.parent(db)),
Item::BuiltinFunction(_) | Item::Intrinsic(_) | Item::Ingot(_) => None,
}
}
Expand Down Expand Up @@ -244,12 +252,32 @@ impl Item {
Item::Trait(id) => id.sink_diagnostics(db, sink),
Item::Impl(id) => id.sink_diagnostics(db, sink),
Item::Function(id) => id.sink_diagnostics(db, sink),
Item::GenericType(_) | Item::BuiltinFunction(_) | Item::Intrinsic(_) => {}
Item::GenericType(_)
| Item::BuiltinFunction(_)
| Item::Intrinsic(_)
| Item::Attribute(_) => {}
Item::Constant(id) => id.sink_diagnostics(db, sink),
Item::Ingot(id) => id.sink_diagnostics(db, sink),
Item::Module(id) => id.sink_diagnostics(db, sink),
}
}

pub fn attributes(&self, db: &dyn AnalyzerDb) -> Vec<AttributeId> {
if let Some(Item::Module(module)) = self.parent(db) {
let mut attributes = vec![];
for item in module.all_items(db).iter() {
if let Item::Attribute(attribute) = item {
attributes.push(*attribute);
} else if item == self {
return attributes;
} else {
attributes = vec![];
}
}
}

vec![]
}
}

pub fn builtin_items() -> IndexMap<SmolStr, Item> {
Expand Down Expand Up @@ -523,6 +551,10 @@ impl ModuleId {
db.module_used_item_map(*self).value
}

pub fn tests(&self, db: &dyn AnalyzerDb) -> Vec<FunctionId> {
db.module_tests(*self)
}

/// Returns `true` if the `item` is in scope of the module.
pub fn is_in_scope(&self, db: &dyn AnalyzerDb, item: Item) -> bool {
if let Some(val) = item.module(db) {
Expand Down Expand Up @@ -678,6 +710,20 @@ impl ModuleId {
.collect::<Vec<_>>()
}

/// All functions, including from submodules and including duplicates
pub fn all_functions(&self, db: &dyn AnalyzerDb) -> Vec<FunctionId> {
self.items(db)
.iter()
.filter_map(|(_, item)| {
if let Item::Function(function) = item {
Some(*function)
} else {
None
}
})
.collect()
}

/// Returns the map of ingot deps, built-ins, and the ingot itself as
/// "ingot".
pub fn global_items(&self, db: &dyn AnalyzerDb) -> IndexMap<SmolStr, Item> {
Expand Down Expand Up @@ -1298,6 +1344,13 @@ impl FunctionId {
pub fn is_contract_func(self, db: &dyn AnalyzerDb) -> bool {
self.sig(db).is_contract_func(db)
}

pub fn is_test(&self, db: &dyn AnalyzerDb) -> bool {
Item::Function(*self)
.attributes(db)
.iter()
.any(|attribute| attribute.name(db) == "test")
}
}

trait FunctionsAsItems {
Expand Down Expand Up @@ -1460,6 +1513,35 @@ impl StructFieldId {
}
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Attribute {
pub ast: Node<SmolStr>,
pub module: ModuleId,
}
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
pub struct AttributeId(pub(crate) u32);
impl_intern_key!(AttributeId);

impl AttributeId {
pub fn data(self, db: &dyn AnalyzerDb) -> Rc<Attribute> {
db.lookup_intern_attribute(self)
}
pub fn span(self, db: &dyn AnalyzerDb) -> Span {
self.data(db).ast.span
}
pub fn name(self, db: &dyn AnalyzerDb) -> SmolStr {
self.data(db).ast.kind.to_owned()
}

pub fn module(self, db: &dyn AnalyzerDb) -> ModuleId {
self.data(db).module
}

pub fn parent(self, db: &dyn AnalyzerDb) -> Item {
Item::Module(self.data(db).module)
}
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Enum {
pub ast: Node<ast::Enum>,
Expand Down
9 changes: 9 additions & 0 deletions crates/analyzer/src/traversal/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,7 @@ fn expr_call_named_thing<T: std::fmt::Display>(
),
))),
NamedThing::Item(Item::Impl(_)) => unreachable!(),
NamedThing::Item(Item::Attribute(_)) => unreachable!(),
NamedThing::Item(Item::Ingot(_)) => Err(FatalError::new(context.error(
&format!("`{}` is not callable", func.kind),
func.span,
Expand Down Expand Up @@ -1378,6 +1379,14 @@ fn expr_call_pure(
);
}

if function.is_test(context.db()) {
context.fancy_error(
&format!("`{}` is a test function", fn_name),
vec![Label::primary(call_span, "test functions are not callable")],
vec![],
);
}

let sig = function.signature(context.db());
let name_span = function.name_span(context.db());
validate_named_args(context, &fn_name, name_span, args, &sig.params)?;
Expand Down
1 change: 1 addition & 0 deletions crates/analyzer/tests/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ fn build_snapshot(db: &dyn AnalyzerDb, module: items::ModuleId) -> String {
| Item::BuiltinFunction(_)
| Item::Intrinsic(_)
| Item::Ingot(_)
| Item::Attribute(_)
| Item::Module(_) => vec![],
})
.collect::<Vec<_>>();
Expand Down
3 changes: 3 additions & 0 deletions crates/analyzer/tests/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,3 +354,6 @@ test_file! { invalid_repeat_length }
test_file! { invalid_struct_pub_qualifier }
test_file! { mut_mistakes }
test_file! { invalid_comparisons }

test_file! { test_call }
test_file! { test_params }
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: crates/analyzer/tests/analysis.rs
expression: "build_snapshot(&db, module)"

---
note:
┌─ abi_decode_complex.fe:2:5
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: crates/analyzer/tests/analysis.rs
expression: "build_snapshot(&db, module)"

---
note:
┌─ abi_encoding_stress.fe:2:5
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: crates/analyzer/tests/analysis.rs
expression: "build_snapshot(&db, module)"

---
note:
┌─ address_bytes10_map.fe:2:5
Expand Down
1 change: 0 additions & 1 deletion crates/analyzer/tests/snapshots/analysis__assert.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: crates/analyzer/tests/analysis.rs
expression: "build_snapshot(&db, module)"

---
note:
┌─ assert.fe:2:5
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: crates/analyzer/tests/analysis.rs
expression: "build_snapshot(&db, module)"

---
note:
┌─ associated_fns.fe:2:5
Expand Down
1 change: 0 additions & 1 deletion crates/analyzer/tests/snapshots/analysis__aug_assign.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: crates/analyzer/tests/analysis.rs
expression: "build_snapshot(&db, module)"

---
note:
┌─ aug_assign.fe:2:5
Expand Down
1 change: 0 additions & 1 deletion crates/analyzer/tests/snapshots/analysis__balances.snap
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: crates/analyzer/tests/analysis.rs
expression: "build_snapshot(&db, module)"

---
note:
┌─ balances.fe:4:5
Expand Down
Loading

0 comments on commit c4e9579

Please sign in to comment.