@@ -200,6 +200,21 @@ 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) &&
208+ _dependencies[importedPath] == source) {
209+ // Throw an error indicating a potential loop.
210+ var sourceUrl = _absoluteUrlToDependency (source);
211+ var importedPathUrl = _absoluteUrlToDependency (importedPath);
212+ throw MigrationException (
213+ 'Dependency loop detected: ${sourceUrl .item1 } -> ${importedPathUrl .item1 }' );
214+ }
215+ _dependencies[source] = importedPath;
216+ }
217+
203218 /// Constructs a new module migration visitor.
204219 ///
205220 /// [importCache] must be the same one used by [references] .
@@ -1239,6 +1254,13 @@ class _ModuleMigrationVisitor extends MigrationVisitor {
12391254 var url = declaration.sourceUrl;
12401255 if (url == currentUrl) return null ;
12411256
1257+ // Trace dependencies for loop detection.
1258+ try {
1259+ _addDependency (currentUrl, url);
1260+ } on Exception catch (e) {
1261+ throw MigrationSourceSpanException (e.toString (), declaration.member.span);
1262+ }
1263+
12421264 // If we can load [declaration] from a library entrypoint URL, do so. Choose
12431265 // the shortest one if there are multiple options.
12441266 var libraryUrls = references.libraries[declaration];
0 commit comments