Skip to content

Commit 4e24af3

Browse files
authored
Add skeleton cli crate (#1044)
1 parent 16c1326 commit 4e24af3

File tree

13 files changed

+259
-205
lines changed

13 files changed

+259
-205
lines changed

Diff for: Cargo.lock

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: crates/driver/src/db.rs

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
use crate::diagnostics::CsDbWrapper;
2+
use camino::Utf8Path;
3+
use codespan_reporting::term::{
4+
self,
5+
termcolor::{BufferWriter, ColorChoice},
6+
};
7+
use common::{
8+
diagnostics::CompleteDiagnostic,
9+
impl_db_traits,
10+
indexmap::IndexSet,
11+
input::{IngotKind, Version},
12+
InputDb, InputFile, InputIngot,
13+
};
14+
use hir::{
15+
analysis_pass::AnalysisPassManager, diagnostics::DiagnosticVoucher, hir_def::TopLevelMod,
16+
lower::map_file_to_mod, HirDb, LowerHirDb, ParsingPass, SpannedHirDb,
17+
};
18+
use hir_analysis::{
19+
name_resolution::{DefConflictAnalysisPass, ImportAnalysisPass, PathAnalysisPass},
20+
ty::{
21+
AdtDefAnalysisPass, BodyAnalysisPass, FuncAnalysisPass, ImplAnalysisPass,
22+
ImplTraitAnalysisPass, TraitAnalysisPass, TypeAliasAnalysisPass,
23+
},
24+
HirAnalysisDb,
25+
};
26+
27+
use crate::diagnostics::ToCsDiag;
28+
29+
#[salsa::db]
30+
pub trait DriverDb:
31+
salsa::Database + HirAnalysisDb + HirDb + LowerHirDb + SpannedHirDb + InputDb
32+
{
33+
fn as_driver_db(&self) -> &dyn DriverDb;
34+
}
35+
36+
#[derive(Default, Clone)]
37+
#[salsa::db]
38+
pub struct DriverDataBase {
39+
storage: salsa::Storage<Self>,
40+
}
41+
impl_db_traits!(
42+
DriverDataBase,
43+
InputDb,
44+
HirDb,
45+
LowerHirDb,
46+
SpannedHirDb,
47+
HirAnalysisDb,
48+
DriverDb
49+
);
50+
51+
impl DriverDataBase {
52+
// TODO: An temporary implementation for ui testing.
53+
pub fn run_on_top_mod<'db>(&'db self, top_mod: TopLevelMod<'db>) -> DiagnosticsCollection<'db> {
54+
self.run_on_file_with_pass_manager(top_mod, initialize_analysis_pass)
55+
}
56+
57+
pub fn run_on_file_with_pass_manager<'db, F>(
58+
&'db self,
59+
top_mod: TopLevelMod<'db>,
60+
pm_builder: F,
61+
) -> DiagnosticsCollection<'db>
62+
where
63+
F: FnOnce(&'db DriverDataBase) -> AnalysisPassManager<'db>,
64+
{
65+
let mut pass_manager = pm_builder(self);
66+
DiagnosticsCollection(pass_manager.run_on_module(top_mod))
67+
}
68+
69+
pub fn standalone(&mut self, file_path: &Utf8Path, source: &str) -> (InputIngot, InputFile) {
70+
let kind = IngotKind::StandAlone;
71+
72+
// We set the ingot version to 0.0.0 for stand-alone file.
73+
let version = Version::new(0, 0, 0);
74+
let root_file = file_path;
75+
let ingot = InputIngot::new(
76+
self,
77+
file_path.parent().unwrap().as_os_str().to_str().unwrap(),
78+
kind,
79+
version,
80+
IndexSet::new(),
81+
);
82+
83+
let file_name = root_file.file_name().unwrap();
84+
let input_file = InputFile::new(self, file_name.into(), source.to_string());
85+
ingot.set_root_file(self, input_file);
86+
ingot.set_files(self, [input_file].into_iter().collect());
87+
(ingot, input_file)
88+
}
89+
90+
pub fn top_mod(&self, ingot: InputIngot, input: InputFile) -> TopLevelMod {
91+
map_file_to_mod(self, ingot, input)
92+
}
93+
}
94+
95+
pub struct DiagnosticsCollection<'db>(Vec<Box<dyn DiagnosticVoucher<'db> + 'db>>);
96+
impl<'db> DiagnosticsCollection<'db> {
97+
pub fn emit(&self, db: &'db DriverDataBase) {
98+
let writer = BufferWriter::stderr(ColorChoice::Auto);
99+
let mut buffer = writer.buffer();
100+
let config = term::Config::default();
101+
102+
for diag in self.finalize(db) {
103+
term::emit(&mut buffer, &config, &CsDbWrapper(db), &diag.to_cs(db)).unwrap();
104+
}
105+
106+
eprintln!("{}", std::str::from_utf8(buffer.as_slice()).unwrap());
107+
}
108+
109+
/// Format the accumulated diagnostics to a string.
110+
pub fn format_diags(&self, db: &'db DriverDataBase) -> String {
111+
let writer = BufferWriter::stderr(ColorChoice::Never);
112+
let mut buffer = writer.buffer();
113+
let config = term::Config::default();
114+
115+
for diag in self.finalize(db) {
116+
term::emit(&mut buffer, &config, &CsDbWrapper(db), &diag.to_cs(db)).unwrap();
117+
}
118+
119+
std::str::from_utf8(buffer.as_slice()).unwrap().to_string()
120+
}
121+
122+
fn finalize(&self, db: &'db DriverDataBase) -> Vec<CompleteDiagnostic> {
123+
let mut diags: Vec<_> = self.0.iter().map(|d| d.to_complete(db)).collect();
124+
diags.sort_by(|lhs, rhs| match lhs.error_code.cmp(&rhs.error_code) {
125+
std::cmp::Ordering::Equal => lhs.primary_span().cmp(&rhs.primary_span()),
126+
ord => ord,
127+
});
128+
diags
129+
}
130+
}
131+
132+
fn initialize_analysis_pass(db: &DriverDataBase) -> AnalysisPassManager<'_> {
133+
let mut pass_manager = AnalysisPassManager::new();
134+
pass_manager.add_module_pass(Box::new(ParsingPass::new(db)));
135+
pass_manager.add_module_pass(Box::new(DefConflictAnalysisPass::new(db)));
136+
pass_manager.add_module_pass(Box::new(ImportAnalysisPass::new(db)));
137+
pass_manager.add_module_pass(Box::new(PathAnalysisPass::new(db)));
138+
pass_manager.add_module_pass(Box::new(AdtDefAnalysisPass::new(db)));
139+
pass_manager.add_module_pass(Box::new(TypeAliasAnalysisPass::new(db)));
140+
pass_manager.add_module_pass(Box::new(TraitAnalysisPass::new(db)));
141+
pass_manager.add_module_pass(Box::new(ImplAnalysisPass::new(db)));
142+
pass_manager.add_module_pass(Box::new(ImplTraitAnalysisPass::new(db)));
143+
pass_manager.add_module_pass(Box::new(FuncAnalysisPass::new(db)));
144+
pass_manager.add_module_pass(Box::new(BodyAnalysisPass::new(db)));
145+
pass_manager
146+
}

Diff for: crates/driver/src/files.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use camino::Utf8PathBuf;
2+
3+
pub const FE_TOML: &str = "fe.toml";
4+
5+
pub fn find_project_root() -> Option<Utf8PathBuf> {
6+
let mut path = Utf8PathBuf::from_path_buf(
7+
std::env::current_dir().expect("Unable to get current directory"),
8+
)
9+
.expect("Expected utf8 path");
10+
11+
loop {
12+
let fe_toml = path.join(FE_TOML);
13+
if fe_toml.is_file() {
14+
return Some(path);
15+
}
16+
17+
if !path.pop() {
18+
break;
19+
}
20+
}
21+
22+
None
23+
}

Diff for: crates/driver/src/lib.rs

+43-141
Original file line numberDiff line numberDiff line change
@@ -1,149 +1,51 @@
1+
pub mod db;
12
pub mod diagnostics;
2-
3-
use std::path;
4-
5-
use codespan_reporting::term::{
6-
self,
7-
termcolor::{BufferWriter, ColorChoice},
8-
};
9-
use common::{
10-
diagnostics::CompleteDiagnostic,
11-
impl_db_traits,
12-
indexmap::IndexSet,
13-
input::{IngotKind, Version},
14-
InputDb, InputFile, InputIngot,
15-
};
16-
pub use diagnostics::CsDbWrapper;
17-
use hir::{
18-
analysis_pass::AnalysisPassManager, diagnostics::DiagnosticVoucher, hir_def::TopLevelMod,
19-
lower::map_file_to_mod, HirDb, LowerHirDb, ParsingPass, SpannedHirDb,
20-
};
21-
use hir_analysis::{
22-
name_resolution::{DefConflictAnalysisPass, ImportAnalysisPass, PathAnalysisPass},
23-
ty::{
24-
AdtDefAnalysisPass, BodyAnalysisPass, FuncAnalysisPass, ImplAnalysisPass,
25-
ImplTraitAnalysisPass, TraitAnalysisPass, TypeAliasAnalysisPass,
26-
},
27-
HirAnalysisDb,
28-
};
29-
30-
use crate::diagnostics::ToCsDiag;
31-
32-
#[salsa::db]
33-
pub trait DriverDb:
34-
salsa::Database + HirAnalysisDb + HirDb + LowerHirDb + SpannedHirDb + InputDb
35-
{
36-
fn as_driver_db(&self) -> &dyn DriverDb;
37-
}
38-
39-
#[derive(Default, Clone)]
40-
#[salsa::db]
41-
pub struct DriverDataBase {
42-
storage: salsa::Storage<Self>,
43-
}
44-
impl_db_traits!(
45-
DriverDataBase,
46-
InputDb,
47-
HirDb,
48-
LowerHirDb,
49-
SpannedHirDb,
50-
HirAnalysisDb,
51-
DriverDb
52-
);
53-
54-
impl DriverDataBase {
55-
// TODO: An temporary implementation for ui testing.
56-
pub fn run_on_top_mod<'db>(&'db self, top_mod: TopLevelMod<'db>) -> DiagnosticsCollection<'db> {
57-
self.run_on_file_with_pass_manager(top_mod, initialize_analysis_pass)
58-
}
59-
60-
pub fn run_on_file_with_pass_manager<'db, F>(
61-
&'db self,
62-
top_mod: TopLevelMod<'db>,
63-
pm_builder: F,
64-
) -> DiagnosticsCollection<'db>
65-
where
66-
F: FnOnce(&'db DriverDataBase) -> AnalysisPassManager<'db>,
67-
{
68-
let mut pass_manager = pm_builder(self);
69-
DiagnosticsCollection(pass_manager.run_on_module(top_mod))
70-
}
71-
72-
pub fn standalone(&mut self, file_path: &path::Path, source: &str) -> (InputIngot, InputFile) {
73-
let kind = IngotKind::StandAlone;
74-
75-
// We set the ingot version to 0.0.0 for stand-alone file.
76-
let version = Version::new(0, 0, 0);
77-
let root_file = file_path;
78-
let ingot = InputIngot::new(
79-
self,
80-
file_path.parent().unwrap().as_os_str().to_str().unwrap(),
81-
kind,
82-
version,
83-
IndexSet::new(),
84-
);
85-
86-
let file_name = root_file.file_name().unwrap().to_str().unwrap();
87-
let input_file = InputFile::new(self, file_name.into(), source.to_string());
88-
ingot.set_root_file(self, input_file);
89-
ingot.set_files(self, [input_file].into_iter().collect());
90-
(ingot, input_file)
91-
}
92-
93-
pub fn top_mod(&self, ingot: InputIngot, input: InputFile) -> TopLevelMod {
94-
map_file_to_mod(self, ingot, input)
95-
}
96-
}
97-
98-
pub struct DiagnosticsCollection<'db>(Vec<Box<dyn DiagnosticVoucher<'db> + 'db>>);
99-
impl<'db> DiagnosticsCollection<'db> {
100-
pub fn emit(&self, db: &'db DriverDataBase) {
101-
let writer = BufferWriter::stderr(ColorChoice::Auto);
102-
let mut buffer = writer.buffer();
103-
let config = term::Config::default();
104-
105-
for diag in self.finalize(db) {
106-
term::emit(&mut buffer, &config, &CsDbWrapper(db), &diag.to_cs(db)).unwrap();
3+
pub mod files;
4+
use camino::Utf8PathBuf;
5+
pub use db::{DriverDataBase, DriverDb};
6+
7+
use clap::{Parser, Subcommand};
8+
use hir::hir_def::TopLevelMod;
9+
10+
pub fn run(opts: &Options) {
11+
match &opts.command {
12+
Command::Build => eprintln!("`fe build` doesn't work at the moment"),
13+
Command::Check { path } => {
14+
if !path.exists() {
15+
eprintln!("file '{}' does not exist", path);
16+
std::process::exit(2);
17+
}
18+
let source = std::fs::read_to_string(path).unwrap();
19+
20+
let mut db = DriverDataBase::default();
21+
let (ingot, file) = db.standalone(path, &source);
22+
let top_mod = db.top_mod(ingot, file);
23+
let diags = db.run_on_top_mod(top_mod);
24+
diags.emit(&db);
10725
}
108-
109-
eprintln!("{}", std::str::from_utf8(buffer.as_slice()).unwrap());
26+
Command::New => eprintln!("`fe new` doesn't work at the moment"),
11027
}
28+
}
11129

112-
/// Format the accumulated diagnostics to a string.
113-
pub fn format_diags(&self, db: &'db DriverDataBase) -> String {
114-
let writer = BufferWriter::stderr(ColorChoice::Never);
115-
let mut buffer = writer.buffer();
116-
let config = term::Config::default();
117-
118-
for diag in self.finalize(db) {
119-
term::emit(&mut buffer, &config, &CsDbWrapper(db), &diag.to_cs(db)).unwrap();
120-
}
121-
122-
std::str::from_utf8(buffer.as_slice()).unwrap().to_string()
123-
}
30+
#[derive(Debug, Clone, Parser)]
31+
#[command(version, about, long_about = None)]
32+
pub struct Options {
33+
#[command(subcommand)]
34+
pub command: Command,
35+
}
12436

125-
fn finalize(&self, db: &'db DriverDataBase) -> Vec<CompleteDiagnostic> {
126-
let mut diags: Vec<_> = self.0.iter().map(|d| d.to_complete(db)).collect();
127-
diags.sort_by(|lhs, rhs| match lhs.error_code.cmp(&rhs.error_code) {
128-
std::cmp::Ordering::Equal => lhs.primary_span().cmp(&rhs.primary_span()),
129-
ord => ord,
130-
});
131-
diags
132-
}
37+
#[derive(Debug, Clone, Subcommand)]
38+
pub enum Command {
39+
Build,
40+
Check {
41+
// #[clap(default_value_t = find_project_root().unwrap_or(Utf8PathBuf::from(".")))]
42+
path: Utf8PathBuf,
43+
},
44+
New,
13345
}
13446

135-
fn initialize_analysis_pass(db: &DriverDataBase) -> AnalysisPassManager<'_> {
136-
let mut pass_manager = AnalysisPassManager::new();
137-
pass_manager.add_module_pass(Box::new(ParsingPass::new(db)));
138-
pass_manager.add_module_pass(Box::new(DefConflictAnalysisPass::new(db)));
139-
pass_manager.add_module_pass(Box::new(ImportAnalysisPass::new(db)));
140-
pass_manager.add_module_pass(Box::new(PathAnalysisPass::new(db)));
141-
pass_manager.add_module_pass(Box::new(AdtDefAnalysisPass::new(db)));
142-
pass_manager.add_module_pass(Box::new(TypeAliasAnalysisPass::new(db)));
143-
pass_manager.add_module_pass(Box::new(TraitAnalysisPass::new(db)));
144-
pass_manager.add_module_pass(Box::new(ImplAnalysisPass::new(db)));
145-
pass_manager.add_module_pass(Box::new(ImplTraitAnalysisPass::new(db)));
146-
pass_manager.add_module_pass(Box::new(FuncAnalysisPass::new(db)));
147-
pass_manager.add_module_pass(Box::new(BodyAnalysisPass::new(db)));
148-
pass_manager
47+
fn _dump_scope_graph(db: &DriverDataBase, top_mod: TopLevelMod) -> String {
48+
let mut s = vec![];
49+
top_mod.scope_graph(db).write_as_dot(db, &mut s).unwrap();
50+
String::from_utf8(s).unwrap()
14951
}

0 commit comments

Comments
 (0)