Index: pkg/analyzer/lib/src/summary/package_bundle_reader.dart |
diff --git a/pkg/analyzer/lib/src/summary/package_bundle_reader.dart b/pkg/analyzer/lib/src/summary/package_bundle_reader.dart |
index 616863fd8f94073679ec3fdd8c09cced3019aeb9..1e443ede7d5842a2ed517e83f544fe0fce2d108a 100644 |
--- a/pkg/analyzer/lib/src/summary/package_bundle_reader.dart |
+++ b/pkg/analyzer/lib/src/summary/package_bundle_reader.dart |
@@ -1,4 +1,5 @@ |
import 'dart:io' as io; |
+import 'dart:math' show min; |
import 'package:analyzer/dart/element/element.dart'; |
import 'package:analyzer/file_system/file_system.dart'; |
@@ -306,6 +307,56 @@ class StoreBasedSummaryResynthesizer extends SummaryResynthesizer { |
} |
/** |
+ * A [ConflictingSummaryException] indicates that two different summaries |
+ * provided to a [SummaryDataStore] conflict. |
+ */ |
+class ConflictingSummaryException implements Exception { |
+ final String duplicatedUri; |
+ final String summary1Uri; |
+ final String summary2Uri; |
+ String _message; |
+ |
+ ConflictingSummaryException(Iterable<String> summaryPaths, this.duplicatedUri, |
+ this.summary1Uri, this.summary2Uri) { |
+ // Paths are often quite long. Find and extract out a common prefix to |
+ // build a more readable error message. |
+ var prefix = _commonPrefix(summaryPaths.toList()); |
+ _message = ''' |
+These summaries conflict because they overlap: |
+- ${summary1Uri.substring(prefix)} |
+- ${summary2Uri.substring(prefix)} |
+Both contain the file: ${duplicatedUri}. |
+This typically indicates an invalid build rule where two or more targets |
+include the same source. |
+ '''; |
+ } |
+ |
+ /// Given a set of file paths, find a common prefix. |
+ int _commonPrefix(List<String> strings) { |
+ if (strings.isEmpty) return 0; |
+ var first = strings.first; |
+ int common = first.length; |
+ for (int i = 1; i < strings.length; ++i) { |
+ var current = strings[i]; |
+ common = min(common, current.length); |
+ for (int j = 0; j < common; ++j) { |
+ if (first[j] != current[j]) { |
+ common = j; |
+ if (common == 0) return 0; |
+ break; |
+ } |
+ } |
+ } |
+ // The prefix should end with a file separator. |
+ var last = |
+ first.substring(0, common).lastIndexOf(io.Platform.pathSeparator); |
+ return last < 0 ? 0 : last + 1; |
+ } |
+ |
+ String toString() => _message; |
+} |
+ |
+/** |
* A [SummaryDataStore] is a container for the data extracted from a set of |
* summary package bundles. It contains maps which can be used to find linked |
* and unlinked summaries by URI. |
@@ -342,6 +393,11 @@ class SummaryDataStore { |
final Map<String, String> uriToSummaryPath = <String, String>{}; |
/** |
+ * List of summary paths. |
+ */ |
+ final Iterable<String> _summaryPaths; |
+ |
+ /** |
* Create a [SummaryDataStore] and populate it with the summaries in |
* [summaryPaths]. If [recordDependencyInfo] is `true`, record |
* [PackageDependencyInfo] for each summary, for later access via |
@@ -349,7 +405,8 @@ class SummaryDataStore { |
*/ |
SummaryDataStore(Iterable<String> summaryPaths, |
{bool recordDependencyInfo: false, ResourceProvider resourceProvider}) |
- : dependencies = |
+ : _summaryPaths = summaryPaths, |
+ dependencies = |
recordDependencyInfo ? <PackageDependencyInfoBuilder>[] : null { |
summaryPaths.forEach((String path) => _fillMaps(path, resourceProvider)); |
} |
@@ -384,6 +441,11 @@ class SummaryDataStore { |
} |
for (int i = 0; i < bundle.unlinkedUnitUris.length; i++) { |
String uri = bundle.unlinkedUnitUris[i]; |
+ if (uriToSummaryPath.containsKey(uri) && |
+ (uriToSummaryPath[uri] != path)) { |
+ throw new ConflictingSummaryException( |
+ _summaryPaths, uri, uriToSummaryPath[uri], path); |
+ } |
uriToSummaryPath[uri] = path; |
addUnlinkedUnit(uri, bundle.unlinkedUnits[i]); |
} |