Skip to content

Commit 79cd8df

Browse files
committed
Add borrow diagnostics AnalysisPass
1 parent d194b7e commit 79cd8df

10 files changed

Lines changed: 414 additions & 211 deletions

File tree

crates/driver/src/db.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use common::{
1111
},
1212
};
1313
use hir::analysis::{
14-
diagnostics::DiagnosticVoucher, initialize_analysis_pass,
15-
semantic::collect_semantic_borrow_diagnostics,
14+
analysis_pass::AnalysisPassManager, diagnostics::DiagnosticVoucher, initialize_analysis_pass,
15+
semantic::SemanticBorrowAnalysisPass,
1616
};
1717
use hir::{
1818
Ingot,
@@ -62,23 +62,37 @@ impl DriverDataBase {
6262
top_mod: TopLevelMod<'db>,
6363
_mode: MirDiagnosticsMode,
6464
) -> Vec<CompleteDiagnostic> {
65-
collect_semantic_borrow_diagnostics(self, top_mod)
65+
let mut pass_manager = initialize_mir_diagnostics_pass();
66+
let mut diagnostics: Vec<_> = pass_manager
67+
.run_on_module(self, top_mod)
68+
.into_iter()
69+
.map(|diag| diag.to_complete(self))
70+
.collect();
71+
sort_and_dedup_complete_diagnostics(&mut diagnostics);
72+
diagnostics
6673
}
6774

6875
pub fn mir_diagnostics_for_ingot<'db>(
6976
&'db self,
7077
ingot: Ingot<'db>,
71-
mode: MirDiagnosticsMode,
78+
_mode: MirDiagnosticsMode,
7279
) -> Vec<CompleteDiagnostic> {
7380
// Empty ingots (e.g. deleted during incremental workspace changes)
7481
// have no root module to analyze.
75-
let Some(root_data) = ingot.module_tree(self).root_data() else {
82+
if ingot.module_tree(self).root_data().is_none() {
7683
return Vec::new();
7784
};
7885
if self.run_on_ingot(ingot).has_errors(self) {
7986
return Vec::new();
8087
}
81-
self.mir_diagnostics_for_top_mod(root_data.top_mod, mode)
88+
let mut pass_manager = initialize_mir_diagnostics_pass();
89+
let mut diagnostics: Vec<_> = pass_manager
90+
.run_on_module_tree(self, ingot.module_tree(self))
91+
.into_iter()
92+
.map(|diag| diag.to_complete(self))
93+
.collect();
94+
sort_and_dedup_complete_diagnostics(&mut diagnostics);
95+
diagnostics
8296
}
8397

8498
pub fn emit_complete_diagnostics(&self, diagnostics: &[CompleteDiagnostic]) {
@@ -112,6 +126,12 @@ impl DriverDataBase {
112126
}
113127
}
114128

129+
fn initialize_mir_diagnostics_pass() -> AnalysisPassManager {
130+
let mut pass_manager = AnalysisPassManager::new();
131+
pass_manager.add_module_pass("SemanticBorrow", Box::new(SemanticBorrowAnalysisPass));
132+
pass_manager
133+
}
134+
115135
pub struct DiagnosticsCollection<'db>(Vec<Box<dyn DiagnosticVoucher + 'db>>);
116136
impl DiagnosticsCollection<'_> {
117137
pub fn is_empty(&self) -> bool {

crates/fe/tests/cli_output.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,6 +1652,52 @@ fn test_cli_test_ingot_discovers_tests_in_non_root_modules() {
16521652
);
16531653
}
16541654

1655+
#[test]
1656+
fn test_cli_test_ingot_reports_mir_diagnostics_in_non_root_modules() {
1657+
let temp = tempdir().expect("tempdir");
1658+
let src = temp.path().join("src");
1659+
fs::create_dir_all(&src).expect("create src");
1660+
fs::write(
1661+
temp.path().join("fe.toml"),
1662+
"[ingot]\nname = \"non_root_mir_diagnostic\"\nversion = \"0.1.0\"\n",
1663+
)
1664+
.expect("write fe.toml");
1665+
fs::write(src.join("lib.fe"), "pub fn root_marker() {}\n").expect("write root module");
1666+
fs::write(
1667+
src.join("helper.fe"),
1668+
r#"
1669+
struct Inner {}
1670+
1671+
fn bad(x: own Inner) {
1672+
let y = x
1673+
let z = x
1674+
}
1675+
1676+
#[test]
1677+
fn test_non_root_move_conflict() {
1678+
bad(Inner {})
1679+
}
1680+
"#,
1681+
)
1682+
.expect("write helper module");
1683+
1684+
let (output, exit_code) = run_fe_main(&["test", temp.path().to_str().expect("temp utf8")]);
1685+
assert_ne!(exit_code, 0, "expected fe test to fail:\n{output}");
1686+
assert!(
1687+
output.contains("move conflict in `fn bad`"),
1688+
"expected non-root MIR diagnostic, got:\n{output}"
1689+
);
1690+
assert_eq!(
1691+
output.matches("move conflict in `fn bad`").count(),
1692+
1,
1693+
"expected non-root MIR diagnostic once, got:\n{output}"
1694+
);
1695+
assert!(
1696+
!output.contains("Failed to emit test"),
1697+
"expected diagnostics preflight before test emission, got:\n{output}"
1698+
);
1699+
}
1700+
16551701
#[test]
16561702
fn test_cli_test_default_project_path_discovers_tests_in_non_root_modules() {
16571703
let fixture_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))

crates/hir/src/analysis/semantic/borrowck/analyses.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use std::convert::Infallible;
22

3-
use common::diagnostics::CompleteDiagnostic;
43
use cranelift_entity::{EntityRef, SecondaryMap};
54
use dataflow::{BackwardCfgAnalysis, ForwardCfgAnalysis, JoinSemiLattice, SparseAnalysis};
65
use rustc_hash::{FxHashMap, FxHashSet};
76

87
use crate::analysis::{
9-
diagnostics::SpannedHirAnalysisDb,
8+
HirAnalysisDb,
109
semantic::{
1110
SBlockId, SLocalId, SemanticInstance,
1211
borrowck::ir::{NEffectArgValue, NExpr, NSStmtKind},
@@ -17,16 +16,16 @@ use crate::analysis::{
1716

1817
use super::{
1918
canon::{BorrowCanonCx, CanonPlace, CfgAdjacency, Loan, LoanId, MovedPlaces, State},
20-
check::{Borrowck, semantic_borrow_summary},
21-
ir::{BorrowInputRef, NormalizedSemanticBody},
19+
check::{Borrowck, semantic_borrow_summary_voucher},
20+
ir::{BorrowInputRef, NormalizedSemanticBody, SemanticBorrowDiagnostic},
2221
};
2322

2423
pub(super) struct BorrowLoanTargetState<'a, 'db> {
2524
pub(super) loans: &'a mut [Loan<'db>],
2625
}
2726

2827
pub(super) struct BorrowLoanTargetAnalysis<'a, 'db> {
29-
db: &'db dyn SpannedHirAnalysisDb,
28+
db: &'db dyn HirAnalysisDb,
3029
instance: SemanticInstance<'db>,
3130
body: &'a NormalizedSemanticBody<'db>,
3231
entry_state: &'a SecondaryMap<SBlockId, State>,
@@ -35,7 +34,7 @@ pub(super) struct BorrowLoanTargetAnalysis<'a, 'db> {
3534

3635
impl<'a, 'db> BorrowLoanTargetAnalysis<'a, 'db> {
3736
pub(super) fn new(
38-
db: &'db dyn SpannedHirAnalysisDb,
37+
db: &'db dyn HirAnalysisDb,
3938
instance: SemanticInstance<'db>,
4039
body: &'a NormalizedSemanticBody<'db>,
4140
entry_state: &'a SecondaryMap<SBlockId, State>,
@@ -80,7 +79,7 @@ impl<'a, 'db> BorrowLoanTargetAnalysis<'a, 'db> {
8079
loans: &mut [Loan<'db>],
8180
state: &State,
8281
stmt: &super::ir::NSStmt<'db>,
83-
) -> Result<bool, CompleteDiagnostic> {
82+
) -> Result<bool, SemanticBorrowDiagnostic<'db>> {
8483
let NSStmtKind::Assign { dst, expr } = &stmt.kind else {
8584
return Ok(false);
8685
};
@@ -103,7 +102,7 @@ impl<'a, 'db> BorrowLoanTargetAnalysis<'a, 'db> {
103102
args,
104103
effect_args,
105104
} => {
106-
let summary = semantic_borrow_summary(
105+
let summary = semantic_borrow_summary_voucher(
107106
self.db,
108107
get_or_build_semantic_instance(self.db, callee.key),
109108
)?;
@@ -159,7 +158,7 @@ impl<'a, 'db> BorrowLoanTargetAnalysis<'a, 'db> {
159158
impl<'a, 'db> SparseAnalysis for BorrowLoanTargetAnalysis<'a, 'db> {
160159
type Node = SBlockId;
161160
type State = BorrowLoanTargetState<'a, 'db>;
162-
type Error = CompleteDiagnostic;
161+
type Error = SemanticBorrowDiagnostic<'db>;
163162

164163
fn node_count(&self) -> usize {
165164
self.body.blocks.len()
@@ -279,7 +278,7 @@ impl<'a, 'db> BorrowMovedStateAnalysis<'a, 'db> {
279278
impl<'db> ForwardCfgAnalysis for BorrowMovedStateAnalysis<'_, 'db> {
280279
type Block = SBlockId;
281280
type State = MovedState<'db>;
282-
type Error = CompleteDiagnostic;
281+
type Error = SemanticBorrowDiagnostic<'db>;
283282

284283
fn block_count(&self) -> usize {
285284
self.borrowck.body.blocks.len()

crates/hir/src/analysis/semantic/borrowck/canon.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
use common::diagnostics::CompleteDiagnostic;
21
use cranelift_entity::SecondaryMap;
32
use dataflow::JoinSemiLattice;
43
use rustc_hash::{FxHashMap, FxHashSet};
54
use smallvec::SmallVec;
65

76
use crate::{
87
analysis::{
9-
diagnostics::SpannedHirAnalysisDb,
8+
HirAnalysisDb,
109
semantic::{SBlockId, SLocalId, SemOrigin, SemanticInstance},
1110
ty::ty_def::BorrowKind,
1211
},
@@ -17,7 +16,7 @@ use super::{
1716
diagnostics::normalized_body_internal_diag,
1817
ir::{
1918
NBorrowRoot, NBorrowRootId, NExpr, NSPlace, NSPlaceRoot, NSProjectionPath, NSStmt,
20-
NSStmtKind, NormalizedBindingLowering, NormalizedSemanticBody,
19+
NSStmtKind, NormalizedBindingLowering, NormalizedSemanticBody, SemanticBorrowDiagnostic,
2120
},
2221
};
2322

@@ -88,7 +87,7 @@ impl JoinSemiLattice for State {
8887
}
8988

9089
pub(super) struct BorrowCanonCx<'a, 'db> {
91-
db: &'db dyn SpannedHirAnalysisDb,
90+
db: &'db dyn HirAnalysisDb,
9291
instance: SemanticInstance<'db>,
9392
body: &'a NormalizedSemanticBody<'db>,
9493
loans: &'a [Loan<'db>],
@@ -97,7 +96,7 @@ pub(super) struct BorrowCanonCx<'a, 'db> {
9796

9897
impl<'a, 'db> BorrowCanonCx<'a, 'db> {
9998
pub(super) fn new(
100-
db: &'db dyn SpannedHirAnalysisDb,
99+
db: &'db dyn HirAnalysisDb,
101100
instance: SemanticInstance<'db>,
102101
body: &'a NormalizedSemanticBody<'db>,
103102
loans: &'a [Loan<'db>],
@@ -224,7 +223,7 @@ impl<'a, 'db> BorrowCanonCx<'a, 'db> {
224223
state: &State,
225224
place: &NSPlace<'db>,
226225
origin: SemOrigin<'db>,
227-
) -> Result<FxHashSet<CanonPlace<'db>>, CompleteDiagnostic> {
226+
) -> Result<FxHashSet<CanonPlace<'db>>, SemanticBorrowDiagnostic<'db>> {
228227
match place.root {
229228
NSPlaceRoot::Root(root) => Ok(FxHashSet::from_iter([CanonPlace {
230229
root: self
@@ -301,7 +300,11 @@ impl<'a, 'db> BorrowCanonCx<'a, 'db> {
301300
.collect()
302301
}
303302

304-
fn internal_diag(&self, origin: SemOrigin<'db>, message: String) -> CompleteDiagnostic {
303+
fn internal_diag(
304+
&self,
305+
origin: SemOrigin<'db>,
306+
message: String,
307+
) -> SemanticBorrowDiagnostic<'db> {
305308
normalized_body_internal_diag(self.db, self.instance, self.body, origin, message)
306309
}
307310
}

0 commit comments

Comments
 (0)