Detect duplicate names in workspace packages (#4232)

diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index a9315fb..562d6af 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -123,7 +123,7 @@
         );
         for (final package in root.transitiveWorkspace) {
           if (identical(pubspecsMet.entries.first.value, package.pubspec)) {
-            validateWorkspaceGraph(root);
+            validateWorkspace(root);
             return (root: root, work: package);
           }
         }
diff --git a/lib/src/package.dart b/lib/src/package.dart
index e1c5208..1cf3034 100644
--- a/lib/src/package.dart
+++ b/lib/src/package.dart
@@ -361,8 +361,10 @@
 }
 
 /// Reports an error if the graph of the workspace rooted at [root] is not a
-/// tree.
-void validateWorkspaceGraph(Package root) {
+/// tree. Or if a package name occurs twice.
+void validateWorkspace(Package root) {
+  if (root.workspaceChildren.isEmpty) return;
+
   final includedFrom = <String, String>{};
   final stack = [root];
 
@@ -382,4 +384,17 @@
     }
     stack.addAll(current.workspaceChildren);
   }
+
+  // Check that the workspace doesn't contain two packages with the same name!
+  final namesSeen = <String, Package>{};
+  for (final package in root.transitiveWorkspace) {
+    final collision = namesSeen[package.name];
+    if (collision != null) {
+      fail('''
+Workspace members must have unique names.
+`${collision.pubspecPath}` and `${package.pubspecPath}` are both called "${package.name}".
+''');
+    }
+    namesSeen[package.name] = package;
+  }
 }
diff --git a/lib/src/package_config.dart b/lib/src/package_config.dart
index 5c40123..d10990f 100644
--- a/lib/src/package_config.dart
+++ b/lib/src/package_config.dart
@@ -50,7 +50,17 @@
     this.generator,
     this.generatorVersion,
     Map<String, dynamic>? additionalProperties,
-  }) : additionalProperties = additionalProperties ?? {};
+  }) : additionalProperties = additionalProperties ?? {} {
+    final names = <String>{};
+    // Sanity check:
+    for (final p in packages) {
+      if (!names.add(p.name)) {
+        throw ArgumentError(
+          'Duplicate name ${p.name} in generated package config',
+        );
+      }
+    }
+  }
 
   /// Create [PackageConfig] from JSON [data].
   ///
diff --git a/test/workspace_test.dart b/test/workspace_test.dart
index e381fb5..d0fa09d 100644
--- a/test/workspace_test.dart
+++ b/test/workspace_test.dart
@@ -1163,6 +1163,41 @@
           'Because myapp depends on both a 2.0.0 and a, version solving failed.',
     );
   });
+
+  test('Reports error if two members of workspace has same name', () async {
+    final server = await servePackages();
+    server.serve('dev_dep', '1.0.0');
+    await dir(appPath, [
+      libPubspec(
+        'myapp',
+        '1.2.3',
+        extras: {
+          'workspace': ['a', 'b'],
+        },
+        sdk: '^3.5.0',
+      ),
+      dir('a', [
+        libPubspec(
+          'a',
+          '1.0.0',
+          resolutionWorkspace: true,
+        ),
+      ]),
+      dir('b', [
+        libPubspec(
+          'a', // Has same name as sibling.
+          '1.0.0',
+          resolutionWorkspace: true,
+        ),
+      ]),
+    ]).create();
+    await pubGet(
+      environment: {'_PUB_TEST_SDK_VERSION': '3.5.0'},
+      error: '''
+Workspace members must have unique names.
+`a${s}pubspec.yaml` and `b${s}pubspec.yaml` are both called "a".''',
+    );
+  });
 }
 
 final s = p.separator;