Skip to content

Commit bac94a4

Browse files
zthCopilot
andauthored
improve errors for circular dependencies (#7940)
* improve errors for circular dependencies * changelog * fix * Update rewatch/src/build/compile/dependency_cycle.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * remove * fix error message * remove replace --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent f3585e2 commit bac94a4

File tree

4 files changed

+69
-17
lines changed

4 files changed

+69
-17
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
#### :nail_care: Polish
2626

27+
- Improve circular dependency errors, and make sure they end up in the compiler log so the editor tooling can surface them. https://github.com/rescript-lang/rescript/pull/7940
28+
2729
#### :house: Internal
2830

2931
- Use AST nodes with locations for fn arguments in the typed tree. https://github.com/rescript-lang/rescript/pull/7873

rewatch/src/build/compile.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -356,11 +356,28 @@ pub fn compile(
356356
.collect::<Vec<(&String, &Module)>>(),
357357
);
358358

359-
compile_errors.push_str(&format!(
360-
"\n{}\n{}\n",
359+
let guidance = "Possible solutions:\n- Extract shared code into a new module both depend on.\n";
360+
let message = format!(
361+
"\n{}\n{}\n{}",
361362
style("Can't continue... Found a circular dependency in your code:").red(),
362-
dependency_cycle::format(&cycle)
363-
))
363+
dependency_cycle::format(&cycle, build_state),
364+
guidance
365+
);
366+
367+
// Append the error to the logs of all packages involved in the cycle,
368+
// so editor tooling can surface it from .compiler.log
369+
let mut touched_packages = AHashSet::<String>::new();
370+
for module_name in cycle.iter() {
371+
if let Some(module) = build_state.get_module(module_name) {
372+
if touched_packages.insert(module.package_name.clone()) {
373+
if let Some(package) = build_state.get_package(&module.package_name) {
374+
logs::append(package, &message);
375+
}
376+
}
377+
}
378+
}
379+
380+
compile_errors.push_str(&message)
364381
}
365382
if !compile_errors.is_empty() {
366383
break;

rewatch/src/build/compile/dependency_cycle.rs

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use super::super::build_types::*;
22
use crate::helpers;
33
use ahash::AHashSet;
4-
use std::collections::{HashMap, HashSet, VecDeque};
4+
use std::{
5+
collections::{HashMap, HashSet, VecDeque},
6+
path::Path,
7+
};
58

69
pub fn find(modules: &Vec<(&String, &Module)>) -> Vec<String> {
710
find_shortest_cycle(modules)
@@ -139,15 +142,43 @@ fn find_cycle_bfs(
139142
None
140143
}
141144

142-
pub fn format(cycle: &[String]) -> String {
143-
let mut cycle = cycle.to_vec();
144-
cycle.reverse();
145-
// add the first module to the end of the cycle
146-
cycle.push(cycle[0].clone());
145+
pub fn format(cycle: &[String], build_state: &BuildCommandState) -> String {
146+
let mut nodes = cycle.to_vec();
147+
if nodes.is_empty() {
148+
return String::new();
149+
}
150+
nodes.reverse();
151+
nodes.push(nodes[0].clone());
152+
153+
let root_path = &build_state.get_root_config().path;
154+
let root = root_path.parent().unwrap_or(root_path.as_path());
147155

148-
cycle
156+
nodes
149157
.iter()
150-
.map(|s| helpers::format_namespaced_module_name(s))
158+
.map(|name| {
159+
let display_name = helpers::format_namespaced_module_name(name);
160+
161+
match build_state.get_module(name) {
162+
Some(Module {
163+
source_type: SourceType::SourceFile(source_file),
164+
package_name,
165+
..
166+
}) => {
167+
if let Some(package) = build_state.get_package(package_name) {
168+
let abs_path = Path::new(&package.path).join(&source_file.implementation.path);
169+
let rel_path = abs_path.strip_prefix(root).unwrap_or(&abs_path).to_string_lossy();
170+
format!("{display_name} ({rel_path})")
171+
} else {
172+
display_name
173+
}
174+
}
175+
Some(Module {
176+
source_type: SourceType::MlMap(_),
177+
..
178+
})
179+
| None => display_name,
180+
}
181+
})
151182
.collect::<Vec<String>>()
152183
.join("\n → ")
153184
}

rewatch/tests/snapshots/dependency-cycle.txt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ The field 'bsc-flags' found in the package config of '@testrepo/deprecated-confi
1212
Use 'compiler-flags' instead.
1313

1414
Can't continue... Found a circular dependency in your code:
15-
Dep01
16-
→ Dep02
17-
→ NS
18-
→ NewNamespace.NS_alias
19-
→ Dep01
15+
Dep01 (packages/dep01/src/Dep01.res)
16+
→ Dep02 (packages/dep02/src/Dep02.res)
17+
→ NS (packages/new-namespace/src/NS.res)
18+
→ NewNamespace.NS_alias (packages/new-namespace/src/NS_alias.res)
19+
→ Dep01 (packages/dep01/src/Dep01.res)
20+
Possible solutions:
21+
- Extract shared code into a new module both depend on.
2022

2123
Incremental build failed. Error:  Failed to Compile. See Errors Above

0 commit comments

Comments
 (0)