Handle build-versions when reformatting ranges (#3170)

diff --git a/lib/src/solver/reformat_ranges.dart b/lib/src/solver/reformat_ranges.dart
index bdb64b8..d40bd6f 100644
--- a/lib/src/solver/reformat_ranges.dart
+++ b/lib/src/solver/reformat_ranges.dart
@@ -4,6 +4,7 @@
 
 // @dart=2.10
 
+import 'package:meta/meta.dart';
 import 'package:pub_semver/pub_semver.dart';
 
 import '../package_name.dart';
@@ -48,7 +49,7 @@
   var range = term.package.constraint as VersionRange;
 
   var min = _reformatMin(versions, range);
-  var tuple = _reformatMax(versions, range);
+  var tuple = reformatMax(versions, range);
   var max = tuple?.first;
   var includeMax = tuple?.last;
 
@@ -84,10 +85,16 @@
 
 /// Returns the new maximum version to use for [range] and whether that maximum
 /// is inclusive, or `null` if it doesn't need to be reformatted.
-Pair<Version, bool> _reformatMax(List<PackageId> versions, VersionRange range) {
+@visibleForTesting
+Pair<Version, bool> reformatMax(List<PackageId> versions, VersionRange range) {
+  // This corresponds to the logic in the constructor of [VersionRange] with
+  // `alwaysIncludeMaxPreRelease = false` for discovering when a max-bound
+  // should not include prereleases.
+
   if (range.max == null) return null;
   if (range.includeMax) return null;
   if (range.max.isPreRelease) return null;
+  if (range.max.build.isNotEmpty) return null;
   if (range.min != null &&
       range.min.isPreRelease &&
       equalsIgnoringPreRelease(range.min, range.max)) {
diff --git a/test/reformat_ranges_test.dart b/test/reformat_ranges_test.dart
new file mode 100644
index 0000000..4af9cf7
--- /dev/null
+++ b/test/reformat_ranges_test.dart
@@ -0,0 +1,57 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.10
+
+import 'package:pub/src/package_name.dart';
+import 'package:pub/src/solver/reformat_ranges.dart';
+import 'package:pub/src/utils.dart';
+import 'package:pub_semver/pub_semver.dart';
+import 'package:test/test.dart';
+
+void main() {
+  test('reformatMax when max has a build identifier', () {
+    expect(
+      reformatMax(
+        [PackageId('abc', null, Version.parse('1.2.3'), null)],
+        VersionRange(
+          min: Version.parse('0.2.4'),
+          max: Version.parse('1.2.4'),
+          alwaysIncludeMaxPreRelease: true,
+        ),
+      ),
+      equals(
+        Pair(
+          Version.parse('1.2.4-0'),
+          false,
+        ),
+      ),
+    );
+    expect(
+      reformatMax(
+        [PackageId('abc', null, Version.parse('1.2.4-3'), null)],
+        VersionRange(
+          min: Version.parse('0.2.4'),
+          max: Version.parse('1.2.4'),
+          alwaysIncludeMaxPreRelease: true,
+        ),
+      ),
+      equals(
+        Pair(
+          Version.parse('1.2.4-3'),
+          true,
+        ),
+      ),
+    );
+    expect(
+        reformatMax(
+          [],
+          VersionRange(
+            max: Version.parse('1.2.4+1'),
+            alwaysIncludeMaxPreRelease: true,
+          ),
+        ),
+        equals(null));
+  });
+}
diff --git a/test/version_solver_test.dart b/test/version_solver_test.dart
index e2136c5..caca301 100644
--- a/test/version_solver_test.dart
+++ b/test/version_solver_test.dart
@@ -31,6 +31,8 @@
   group('override', override);
   group('downgrade', downgrade);
   group('features', features, skip: true);
+
+  group('regressions', regressions);
 }
 
 void basicGraph() {
@@ -3014,3 +3016,22 @@
 
   expect(ids, isEmpty, reason: 'Expected no additional packages.');
 }
+
+void regressions() {
+  test('reformatRanges with a build', () async {
+    await servePackages((b) {
+      b.serve('integration_test', '1.0.1',
+          deps: {'vm_service': '>= 4.2.0 <6.0.0'});
+      b.serve('integration_test', '1.0.2+2',
+          deps: {'vm_service': '>= 4.2.0 <7.0.0'});
+
+      b.serve('vm_service', '7.3.0');
+    });
+    await d.appDir({'integration_test': '^1.0.2'}).create();
+    await expectResolves(
+      error: contains(
+        'Because no versions of integration_test match >=1.0.2 <1.0.2+2',
+      ),
+    );
+  });
+}