@@ -200,6 +200,19 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
200200 /// The values of the --forward flag.
201201 final Set <ForwardType > forwards;
202202
203+ // Maps direct and indirect dependencies to prevent any potential loops.
204+ final Map <Uri , Uri > _dependencies = {};
205+
206+ void _addDependency (Uri source, Uri importedPath) {
207+ if (_dependencies.containsKey (importedPath) && _dependencies[importedPath] == source) {
208+ // Throw an error indicating a potential loop.
209+ var sourceUrl = _absoluteUrlToDependency (source);
210+ var importedPathUrl = _absoluteUrlToDependency (importedPath);
211+ throw MigrationException ('Dependency loop detected: ${sourceUrl .item1 } -> ${importedPathUrl .item1 }' );
212+ }
213+ _dependencies[source] = importedPath;
214+ }
215+
203216 /// Constructs a new module migration visitor.
204217 ///
205218 /// [importCache] must be the same one used by [references] .
@@ -1239,6 +1252,13 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
12391252 var url = declaration.sourceUrl;
12401253 if (url == currentUrl) return null ;
12411254
1255+ // Trace dependencies for loop detection.
1256+ try {
1257+ _addDependency (currentUrl, url);
1258+ } on Exception catch (e) {
1259+ throw MigrationSourceSpanException (e.toString (), declaration.member.span);
1260+ }
1261+
12421262 // If we can load [declaration] from a library entrypoint URL, do so. Choose
12431263 // the shortest one if there are multiple options.
12441264 var libraryUrls = references.libraries[declaration];
0 commit comments