@@ -209,57 +209,35 @@ impl ChunkOptimizationGraph {
209209 source_chunk_idx : ChunkIdx ,
210210 target_chunk_idx : ChunkIdx ,
211211 ) -> bool {
212- let source_has_deps = !self . chunks [ source_chunk_idx] . dependencies . is_empty ( ) ;
213-
214- if source_has_deps {
215- // When source has dependencies, only BFS from source's deps.
216- // Including target's deps causes false positives because the simulation
217- // finds that target can trivially "reach itself" through any transitive
218- // dependency that also depends on source.
219- let mut queue: VecDeque < ChunkIdx > =
220- self . chunks [ source_chunk_idx] . dependencies . iter ( ) . copied ( ) . collect ( ) ;
221- let mut visited = FxHashSet :: default ( ) ;
212+ // Start BFS from the combined deps of source and target.
213+ let mut queue: VecDeque < ChunkIdx > = self . chunks [ source_chunk_idx]
214+ . dependencies
215+ . iter ( )
216+ . chain ( self . chunks [ target_chunk_idx] . dependencies . iter ( ) )
217+ . copied ( )
218+ . collect ( ) ;
219+ let mut visited = FxHashSet :: default ( ) ;
222220
223- while let Some ( chunk_idx) = queue. pop_front ( ) {
224- if chunk_idx == target_chunk_idx {
225- return true ;
226- }
227- if !visited. insert ( chunk_idx) {
228- continue ;
229- }
230- for & dep in & self . chunks [ chunk_idx] . dependencies {
231- if !visited. contains ( & dep) {
232- queue. push_back ( dep) ;
233- }
234- }
221+ while let Some ( chunk_idx) = queue. pop_front ( ) {
222+ if chunk_idx == target_chunk_idx {
223+ return true ;
235224 }
236- false
237- } else {
238- // When source has no dependencies (e.g., runtime chunk), we must check
239- // from target's deps with post-merge simulation to detect cycles like
240- // target -> chunk_A -> source(=target after merge).
241- let mut queue: VecDeque < ChunkIdx > =
242- self . chunks [ target_chunk_idx] . dependencies . iter ( ) . copied ( ) . collect ( ) ;
243- let mut visited = FxHashSet :: default ( ) ;
244-
245- while let Some ( chunk_idx) = queue. pop_front ( ) {
246- if chunk_idx == target_chunk_idx {
247- return true ;
248- }
249- if !visited. insert ( chunk_idx) {
250- continue ;
251- }
252- for & dep in & self . chunks [ chunk_idx] . dependencies {
253- if !visited. contains ( & dep) {
254- queue. push_back ( dep) ;
255- }
256- }
257- if self . chunks [ chunk_idx] . dependencies . contains ( & source_chunk_idx) {
258- queue. push_back ( target_chunk_idx) ;
225+ if !visited. insert ( chunk_idx) {
226+ continue ;
227+ }
228+ for & dep in & self . chunks [ chunk_idx] . dependencies {
229+ if !visited. contains ( & dep) {
230+ queue. push_back ( dep) ;
259231 }
260232 }
261- false
233+ // Any chunk that depends on source will depend on target after the merge.
234+ // Simulate this by also queuing target when we encounter such a chunk.
235+ if self . chunks [ chunk_idx] . dependencies . contains ( & source_chunk_idx) {
236+ queue. push_back ( target_chunk_idx) ;
237+ }
262238 }
239+
240+ false
263241 }
264242
265243 /// Returns true if `from` can transitively reach `to` through the dependency graph.
0 commit comments