Skip to content

Commit

Permalink
Multi-file support
Browse files Browse the repository at this point in the history
  • Loading branch information
g-r-a-n-t committed Nov 23, 2021
1 parent a8dfc1b commit 4fca2e4
Show file tree
Hide file tree
Showing 78 changed files with 2,560 additions and 754 deletions.
28 changes: 28 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 21 additions & 4 deletions crates/abi/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,11 @@ fn components(db: &dyn AnalyzerDb, typ: &types::FixedSize) -> Vec<Component> {
#[cfg(test)]
mod tests {
use crate::builder;
use fe_analyzer::Db;
use fe_analyzer::namespace::items::{Global, Module, ModuleContext, ModuleFileContent};
use fe_analyzer::{AnalyzerDb, TestDb};
use fe_common::files::SourceFileId;
use fe_parser::{grammar::module::parse_module, parse_code_chunk};
use std::rc::Rc;

#[test]
fn build_contract_abi() {
Expand All @@ -146,11 +149,25 @@ contract Foo:
pub fn bar(x: u256) -> Array<u256, 10>:
revert"#;

let module = parse_code_chunk(parse_module, contract)
let ast = parse_code_chunk(parse_module, contract)
.expect("unable to build module AST")
.kind;
let db = Db::default();
let module_id = fe_analyzer::analyze(&db, module).expect("failed to analyze source");
let db = TestDb::default();

let global = Global::default();
let global_id = db.intern_global(Rc::new(global));

let module = Module {
name: "test_module".to_string(),
context: ModuleContext::Global(global_id),
file_content: ModuleFileContent::File {
file: SourceFileId(0),
},
ast,
};
let module_id = db.intern_module(Rc::new(module));

fe_analyzer::analyze_module(&db, module_id).expect("failed to analyze source");
let abis = builder::module(&db, module_id).expect("unable to build ABI");

if let Some(abi) = abis.get("Foo") {
Expand Down
5 changes: 5 additions & 0 deletions crates/analyzer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::AnalyzerDb;
use fe_common::diagnostics::Diagnostic;
pub use fe_common::diagnostics::Label;
use fe_common::Span;
use fe_parser::ast;
use fe_parser::node::NodeId;
use indexmap::IndexMap;
use std::collections::HashMap;
Expand All @@ -28,6 +29,7 @@ impl<T> Analysis<T> {

pub trait AnalyzerContext {
fn resolve_name(&self, name: &str) -> Option<NamedThing>;
fn resolve_path(&mut self, path: &ast::Path) -> Option<NamedThing>;
fn add_diagnostic(&mut self, diag: Diagnostic);
fn db(&self) -> &dyn AnalyzerDb;

Expand Down Expand Up @@ -157,6 +159,9 @@ impl AnalyzerContext for TempContext {
fn add_diagnostic(&mut self, diag: Diagnostic) {
self.diagnostics.push(diag)
}
fn resolve_path(&mut self, _path: &ast::Path) -> Option<NamedThing> {
panic!("TempContext can't resolve paths")
}
}

/// Indicates where an expression is stored.
Expand Down
44 changes: 36 additions & 8 deletions crates/analyzer/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::context::{Analysis, FunctionBody};
use crate::errors::TypeError;
use crate::namespace::items::{
self, ContractFieldId, ContractId, EventId, FunctionId, Item, ModuleConstantId, ModuleId,
StructFieldId, StructId, TypeAliasId,
self, ContractFieldId, ContractId, EventId, FunctionId, GlobalId, IngotId, Item,
ModuleConstantId, ModuleId, StructFieldId, StructId, TypeAliasId,
};
use crate::namespace::types;
use indexmap::IndexMap;
use fe_common::Span;
use fe_parser::ast;
use fe_parser::node::Node;
use indexmap::map::IndexMap;
use std::rc::Rc;

mod queries;
Expand All @@ -28,6 +31,10 @@ macro_rules! impl_intern_key {

#[salsa::query_group(AnalyzerDbStorage)]
pub trait AnalyzerDb {
#[salsa::interned]
fn intern_global(&self, data: Rc<items::Global>) -> GlobalId;
#[salsa::interned]
fn intern_ingot(&self, data: Rc<items::Ingot>) -> IngotId;
#[salsa::interned]
fn intern_module(&self, data: Rc<items::Module>) -> ModuleId;
#[salsa::interned]
Expand All @@ -47,17 +54,38 @@ pub trait AnalyzerDb {
#[salsa::interned]
fn intern_event(&self, data: Rc<items::Event>) -> EventId;

// Ingot
#[salsa::invoke(queries::ingots::ingot_all_modules)]
fn ingot_all_modules(&self, ingot: IngotId) -> Rc<Vec<ModuleId>>;
#[salsa::invoke(queries::ingots::ingot_main_module)]
fn ingot_main_module(&self, ingot: IngotId) -> Analysis<Option<ModuleId>>;

// Module
#[salsa::invoke(queries::module::module_all_items)]
fn module_all_items(&self, module: ModuleId) -> Rc<Vec<Item>>;
#[salsa::invoke(queries::module::module_item_map)]
fn module_item_map(&self, module: ModuleId) -> Analysis<Rc<IndexMap<String, Item>>>;
#[salsa::invoke(queries::module::module_imported_item_map)]
fn module_imported_item_map(&self, module: ModuleId) -> Rc<IndexMap<String, Item>>;
#[salsa::invoke(queries::module::module_contracts)]
fn module_contracts(&self, module: ModuleId) -> Rc<Vec<ContractId>>;
#[salsa::invoke(queries::module::module_structs)]
fn module_structs(&self, module: ModuleId) -> Rc<Vec<StructId>>;
#[salsa::invoke(queries::module::module_used_item_map)]
fn module_used_item_map(
&self,
module: ModuleId,
) -> Analysis<Rc<IndexMap<String, (Span, Item)>>>;
#[salsa::invoke(queries::module::module_resolve_use_tree)]
fn module_resolve_use_tree(
&self,
module: ModuleId,
tree: Node<ast::UseTree>,
) -> Analysis<Rc<IndexMap<String, (Span, Item)>>>;
#[salsa::invoke(queries::module::module_parent_module)]
fn module_parent_module(&self, module: ModuleId) -> Option<ModuleId>;
#[salsa::invoke(queries::module::module_adjacent_modules)]
fn module_adjacent_modules(&self, module: ModuleId) -> Rc<IndexMap<String, ModuleId>>;
#[salsa::invoke(queries::module::module_sub_modules)]
fn module_sub_modules(&self, module: ModuleId) -> Rc<IndexMap<String, ModuleId>>;

// Module Constant
#[salsa::invoke(queries::module::module_constant_type)]
Expand Down Expand Up @@ -127,7 +155,7 @@ pub trait AnalyzerDb {

#[salsa::database(AnalyzerDbStorage)]
#[derive(Default)]
pub struct Db {
storage: salsa::Storage<Db>,
pub struct TestDb {
storage: salsa::Storage<TestDb>,
}
impl salsa::Database for Db {}
impl salsa::Database for TestDb {}
1 change: 1 addition & 0 deletions crates/analyzer/src/db/queries.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod contracts;
pub mod events;
pub mod functions;
pub mod ingots;
pub mod module;
pub mod structs;
pub mod types;
105 changes: 105 additions & 0 deletions crates/analyzer/src/db/queries/ingots.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use crate::context::Analysis;
use crate::namespace::items::{IngotId, Module, ModuleContext, ModuleFileContent, ModuleId};
use crate::AnalyzerDb;
use fe_common::diagnostics::{Diagnostic, Severity};
use fe_parser::ast;
use indexmap::set::IndexSet;
use std::path::Path;
use std::rc::Rc;

pub fn ingot_all_modules(db: &dyn AnalyzerDb, ingot_id: IngotId) -> Rc<Vec<ModuleId>> {
let ingot = &ingot_id.data(db);

let file_modules = ingot
.fe_files
.values()
.into_iter()
.map(|(file, ast)| {
let module = Module {
name: Path::new(&file.name)
.file_stem()
.expect("file does not have stem")
.to_str()
.expect("could not convert file stem to string")
.to_string(),
ast: ast.clone(),
file_content: ModuleFileContent::File { file: file.id },
context: ModuleContext::Ingot(ingot_id),
};

db.intern_module(Rc::new(module))
})
.collect::<Vec<_>>();

let dir_modules = ingot
.fe_files
.values()
.into_iter()
.map(|(file, _)| {
Path::new(&file.name)
.parent()
.expect("file does not have parent path")
})
.collect::<IndexSet<_>>()
.into_iter()
.map(|dir| {
let module = Module {
name: dir
.file_name()
.expect("missing file name")
.to_str()
.expect("could not convert dir name to string")
.to_string(),
ast: ast::Module { body: vec![] },
context: ModuleContext::Ingot(ingot_id),
file_content: ModuleFileContent::Dir {
dir_path: dir
.to_str()
.expect("could not convert dir path to string")
.to_string(),
},
};

db.intern_module(Rc::new(module))
})
.collect::<Vec<ModuleId>>();

let all_modules = [file_modules, dir_modules].concat();
Rc::new(all_modules)
}

pub fn ingot_main_module(db: &dyn AnalyzerDb, ingot_id: IngotId) -> Analysis<Option<ModuleId>> {
let main_id = ingot_id
.all_modules(db)
.iter()
.find(|module_id| {
module_id.name(db) == "main" && {
if let Some(parent_id) = module_id.parent_module(db) {
parent_id.name(db) == "src"
} else {
false
}
}
})
.copied();

Analysis {
value: main_id,
diagnostics: Rc::new({
if main_id.is_none() {
vec![Diagnostic {
severity: Severity::Error,
message: format!(
"The ingot named \"{}\" is missing a main module. \
\nPlease add a `src/main.fe` file to the base directory.",
ingot_id.name(db)
),
labels: vec![],
notes: vec![],
}]
} else {
vec![]
}
}),
}
}
Loading

0 comments on commit 4fca2e4

Please sign in to comment.