Skip to content

conflict analysis rework #1079

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 19 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions Cargo.lock

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

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ glob = "0.3.2"
rustc-hash = "2.1.0"
num-bigint = "0.4"
paste = "1.0.15"
salsa = { git = "https://github.com/salsa-rs/salsa", rev = "296a8c78da1b54c76ff5795eb4c1e3fe2467e9fc" }
salsa = "0.20"
smallvec = { version = "2.0.0-alpha.10" }
smallvec1 = { version = "1", package = "smallvec" }
semver = "1.0.24"
thin-vec = "0.2.14"
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
tracing-tree = "0.4.0"
Expand Down
16 changes: 1 addition & 15 deletions crates/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub mod ingot;
pub mod input;
pub use input::{InputFile, InputIngot};

#[derive(Embed)]
#[derive(rust_embed::Embed)]
#[folder = "../../library/core"]
pub(crate) struct Core;

Expand All @@ -14,17 +14,3 @@ pub trait InputDb: salsa::Database {}

#[salsa::db]
impl<T> InputDb for T where T: salsa::Database {}

#[doc(hidden)]
pub use paste::paste;
use rust_embed::Embed;

#[macro_export]
macro_rules! impl_db_traits {
($db_type:ty, $($trait_name:ident),+ $(,)?) => {
#[salsa::db]
impl salsa::Database for $db_type {
fn salsa_event(&self, _event: &dyn Fn() -> salsa::Event) {}
}
};
}
72 changes: 32 additions & 40 deletions crates/driver/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ use hir::{
use hir_analysis::{
analysis_pass::{AnalysisPassManager, ParsingPass},
diagnostics::DiagnosticVoucher,
name_resolution::{DefConflictAnalysisPass, ImportAnalysisPass, PathAnalysisPass},
name_resolution::{ImportAnalysisPass, PathAnalysisPass},
ty::{
AdtDefAnalysisPass, BodyAnalysisPass, FuncAnalysisPass, ImplAnalysisPass,
ImplTraitAnalysisPass, TraitAnalysisPass, TypeAliasAnalysisPass,
AdtDefAnalysisPass, BodyAnalysisPass, DefConflictAnalysisPass, FuncAnalysisPass,
ImplAnalysisPass, ImplTraitAnalysisPass, TraitAnalysisPass, TypeAliasAnalysisPass,
},
};

Expand All @@ -33,50 +33,42 @@ impl salsa::Database for DriverDataBase {
impl DriverDataBase {
// TODO: An temporary implementation for ui testing.
pub fn run_on_top_mod<'db>(&'db self, top_mod: TopLevelMod<'db>) -> DiagnosticsCollection<'db> {
self.run_on_file_with_pass_manager(top_mod, initialize_analysis_pass)
self.run_on_file_with_pass_manager(top_mod, initialize_analysis_pass())
}

pub fn run_on_file_with_pass_manager<'db, F>(
pub fn run_on_file_with_pass_manager<'db>(
&'db self,
top_mod: TopLevelMod<'db>,
pm_builder: F,
) -> DiagnosticsCollection<'db>
where
F: FnOnce(&'db DriverDataBase) -> AnalysisPassManager<'db>,
{
let mut pass_manager = pm_builder(self);
DiagnosticsCollection(pass_manager.run_on_module(top_mod))
mut pass_manager: AnalysisPassManager,
) -> DiagnosticsCollection<'db> {
DiagnosticsCollection(pass_manager.run_on_module(self, top_mod))
}

pub fn run_on_ingot(&self, ingot: InputIngot) -> DiagnosticsCollection {
self.run_on_ingot_with_pass_manager(ingot, initialize_analysis_pass)
self.run_on_ingot_with_pass_manager(ingot, initialize_analysis_pass())
}

pub fn run_on_ingot_with_pass_manager<'db, F>(
&'db self,
pub fn run_on_ingot_with_pass_manager(
&self,
ingot: InputIngot,
pm_builder: F,
) -> DiagnosticsCollection<'db>
where
F: FnOnce(&'db DriverDataBase) -> AnalysisPassManager<'db>,
{
mut pass_manager: AnalysisPassManager,
) -> DiagnosticsCollection {
let tree = module_tree(self, ingot);
let mut pass_manager = pm_builder(self);
DiagnosticsCollection(pass_manager.run_on_module_tree(tree))
DiagnosticsCollection(pass_manager.run_on_module_tree(self, tree))
}

pub fn top_mod(&self, ingot: InputIngot, input: InputFile) -> TopLevelMod {
map_file_to_mod(self, ingot, input)
}
}

pub struct DiagnosticsCollection<'db>(Vec<Box<dyn DiagnosticVoucher<'db> + 'db>>);
impl<'db> DiagnosticsCollection<'db> {
pub struct DiagnosticsCollection<'db>(Vec<Box<dyn DiagnosticVoucher + 'db>>);
impl DiagnosticsCollection<'_> {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

pub fn emit(&self, db: &'db DriverDataBase) {
pub fn emit(&self, db: &DriverDataBase) {
let writer = BufferWriter::stderr(ColorChoice::Auto);
let mut buffer = writer.buffer();
let config = term::Config::default();
Expand All @@ -89,7 +81,7 @@ impl<'db> DiagnosticsCollection<'db> {
}

/// Format the accumulated diagnostics to a string.
pub fn format_diags(&self, db: &'db DriverDataBase) -> String {
pub fn format_diags(&self, db: &DriverDataBase) -> String {
let writer = BufferWriter::stderr(ColorChoice::Never);
let mut buffer = writer.buffer();
let config = term::Config::default();
Expand All @@ -101,8 +93,8 @@ impl<'db> DiagnosticsCollection<'db> {
std::str::from_utf8(buffer.as_slice()).unwrap().to_string()
}

fn finalize(&self, db: &'db DriverDataBase) -> Vec<CompleteDiagnostic> {
let mut diags: Vec<_> = self.0.iter().map(|d| d.to_complete(db)).collect();
fn finalize(&self, db: &DriverDataBase) -> Vec<CompleteDiagnostic> {
let mut diags: Vec<_> = self.0.iter().map(|d| d.as_ref().to_complete(db)).collect();
diags.sort_by(|lhs, rhs| match lhs.error_code.cmp(&rhs.error_code) {
std::cmp::Ordering::Equal => lhs.primary_span().cmp(&rhs.primary_span()),
ord => ord,
Expand All @@ -111,18 +103,18 @@ impl<'db> DiagnosticsCollection<'db> {
}
}

fn initialize_analysis_pass(db: &DriverDataBase) -> AnalysisPassManager<'_> {
fn initialize_analysis_pass() -> AnalysisPassManager {
let mut pass_manager = AnalysisPassManager::new();
pass_manager.add_module_pass(Box::new(ParsingPass::new(db)));
pass_manager.add_module_pass(Box::new(DefConflictAnalysisPass::new(db)));
pass_manager.add_module_pass(Box::new(ImportAnalysisPass::new(db)));
pass_manager.add_module_pass(Box::new(PathAnalysisPass::new(db)));
pass_manager.add_module_pass(Box::new(AdtDefAnalysisPass::new(db)));
pass_manager.add_module_pass(Box::new(TypeAliasAnalysisPass::new(db)));
pass_manager.add_module_pass(Box::new(TraitAnalysisPass::new(db)));
pass_manager.add_module_pass(Box::new(ImplAnalysisPass::new(db)));
pass_manager.add_module_pass(Box::new(ImplTraitAnalysisPass::new(db)));
pass_manager.add_module_pass(Box::new(FuncAnalysisPass::new(db)));
pass_manager.add_module_pass(Box::new(BodyAnalysisPass::new(db)));
pass_manager.add_module_pass(Box::new(ParsingPass {}));
pass_manager.add_module_pass(Box::new(DefConflictAnalysisPass {}));
pass_manager.add_module_pass(Box::new(ImportAnalysisPass {}));
pass_manager.add_module_pass(Box::new(PathAnalysisPass {}));
pass_manager.add_module_pass(Box::new(AdtDefAnalysisPass {}));
pass_manager.add_module_pass(Box::new(TypeAliasAnalysisPass {}));
pass_manager.add_module_pass(Box::new(TraitAnalysisPass {}));
pass_manager.add_module_pass(Box::new(ImplAnalysisPass {}));
pass_manager.add_module_pass(Box::new(ImplTraitAnalysisPass {}));
pass_manager.add_module_pass(Box::new(FuncAnalysisPass {}));
pass_manager.add_module_pass(Box::new(BodyAnalysisPass {}));
pass_manager
}
2 changes: 1 addition & 1 deletion crates/driver/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl<T> SpannedInputDb for T where T: SpannedHirAnalysisDb + InputDb {}

impl<T> ToCsDiag for T
where
T: for<'db> DiagnosticVoucher<'db>,
T: DiagnosticVoucher,
{
fn to_cs(&self, db: &dyn SpannedInputDb) -> cs_diag::Diagnostic<InputFile> {
let complete = self.to_complete(db);
Expand Down
2 changes: 2 additions & 0 deletions crates/hir-analysis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ num-bigint.workspace = true
rustc-hash.workspace = true
salsa.workspace = true
smallvec.workspace = true
smallvec1.workspace = true
thin-vec.workspace = true

common.workspace = true
test-utils.workspace = true
Expand Down
54 changes: 25 additions & 29 deletions crates/hir-analysis/src/analysis_pass.rs
Original file line number Diff line number Diff line change
@@ -1,77 +1,73 @@
use crate::diagnostics::DiagnosticVoucher;
use crate::{diagnostics::DiagnosticVoucher, HirAnalysisDb};
use hir::{
hir_def::{ModuleTree, TopLevelMod},
lower::parse_file_impl,
HirDb, ParseErrorAccumulator,
ParserError,
};

/// All analysis passes that run analysis on the HIR top level module
/// granularity should implement this trait.
pub trait ModuleAnalysisPass<'db> {
fn run_on_module(
pub trait ModuleAnalysisPass {
fn run_on_module<'db>(
&mut self,
db: &'db dyn HirAnalysisDb,
top_mod: TopLevelMod<'db>,
) -> Vec<Box<dyn DiagnosticVoucher<'db> + 'db>>;
) -> Vec<Box<dyn DiagnosticVoucher + 'db>>;
}

#[derive(Default)]
pub struct AnalysisPassManager<'db> {
module_passes: Vec<Box<dyn ModuleAnalysisPass<'db> + 'db>>,
pub struct AnalysisPassManager {
module_passes: Vec<Box<dyn ModuleAnalysisPass>>,
}

impl<'db> AnalysisPassManager<'db> {
impl AnalysisPassManager {
pub fn new() -> Self {
Self::default()
}

pub fn add_module_pass(&mut self, pass: Box<dyn ModuleAnalysisPass<'db> + 'db>) {
pub fn add_module_pass(&mut self, pass: Box<dyn ModuleAnalysisPass>) {
self.module_passes.push(pass);
}

pub fn run_on_module(
pub fn run_on_module<'db>(
&mut self,
db: &'db dyn HirAnalysisDb,
top_mod: TopLevelMod<'db>,
) -> Vec<Box<dyn DiagnosticVoucher<'db> + 'db>> {
) -> Vec<Box<dyn DiagnosticVoucher + 'db>> {
let mut diags = vec![];
for pass in self.module_passes.iter_mut() {
diags.extend(pass.run_on_module(top_mod));
diags.extend(pass.run_on_module(db, top_mod));
}
diags
}

pub fn run_on_module_tree(
pub fn run_on_module_tree<'db>(
&mut self,
db: &'db dyn HirAnalysisDb,
tree: &'db ModuleTree<'db>,
) -> Vec<Box<dyn DiagnosticVoucher<'db> + 'db>> {
) -> Vec<Box<dyn DiagnosticVoucher + 'db>> {
let mut diags = vec![];
for module in tree.all_modules() {
for pass in self.module_passes.iter_mut() {
diags.extend(pass.run_on_module(module));
diags.extend(pass.run_on_module(db, module));
}
}
diags
}
}

#[derive(Clone, Copy)]
pub struct ParsingPass<'db> {
db: &'db dyn HirDb,
}

impl<'db> ParsingPass<'db> {
pub fn new(db: &'db dyn HirDb) -> Self {
Self { db }
}
}
pub struct ParsingPass {}

impl<'db> ModuleAnalysisPass<'db> for ParsingPass<'db> {
fn run_on_module(
impl ModuleAnalysisPass for ParsingPass {
fn run_on_module<'db>(
&mut self,
db: &'db dyn HirAnalysisDb,
top_mod: TopLevelMod<'db>,
) -> Vec<Box<dyn DiagnosticVoucher<'db> + 'db>> {
parse_file_impl::accumulated::<ParseErrorAccumulator>(self.db, top_mod)
) -> Vec<Box<dyn DiagnosticVoucher>> {
parse_file_impl::accumulated::<ParserError>(db, top_mod)
.into_iter()
.map(|d| Box::new(d.0.clone()) as _)
.map(|d| Box::new(d.clone()) as _)
.collect::<Vec<_>>()
}
}
Loading