@@ -8,18 +8,25 @@ import 'package:_fe_analyzer_shared/src/util/dependency_walker.dart'
88 as graph
99 show DependencyWalker, Node;
1010import 'package:analyzer/src/dart/analysis/file_state.dart' ;
11+ import 'package:analyzer/src/dart/sdk/sdk.dart' ;
12+ import 'package:analyzer/src/generated/source.dart' ;
1113import 'package:analyzer/src/summary/api_signature.dart' ;
1214import 'package:analyzer/src/utilities/extensions/collection.dart' ;
1315import 'package:collection/collection.dart' ;
1416
1517/// Ensure that the `FileState._libraryCycle` for the [file] and anything it
1618/// depends on is computed.
17- void computeLibraryCycle (
18- bool withFineDependencies,
19- Uint32List salt,
20- LibraryFileKind file,
21- ) {
22- var libraryWalker = _LibraryWalker (withFineDependencies, salt);
19+ void computeLibraryCycle ({
20+ required bool withFineDependencies,
21+ required Uint32List saltForElements,
22+ required SourceFactory sourceFactory,
23+ required LibraryFileKind file,
24+ }) {
25+ var libraryWalker = _LibraryWalker (
26+ withFineDependencies: withFineDependencies,
27+ saltForElements: saltForElements,
28+ sourceFactory: sourceFactory,
29+ );
2330 libraryWalker.walk (libraryWalker.getNode (file));
2431}
2532
@@ -36,6 +43,9 @@ class LibraryCycle {
3643 /// The URIs of [libraries] .
3744 final Set <Uri > libraryUris;
3845
46+ /// The transitive set of package names that this cycle references.
47+ final Set <String > transitivePackages;
48+
3949 /// The library cycles that this cycle references directly.
4050 final Set <LibraryCycle > directDependencies;
4151
@@ -72,6 +82,7 @@ class LibraryCycle {
7282 required this .withFineDependencies,
7383 required this .libraries,
7484 required this .libraryUris,
85+ required this .transitivePackages,
7586 required this .directDependencies,
7687 required this .apiSignature,
7788 required this .manifestSignature,
@@ -153,10 +164,15 @@ class _LibraryNode extends graph.Node<_LibraryNode> {
153164/// sorted [LibraryCycle] s.
154165class _LibraryWalker extends graph.DependencyWalker <_LibraryNode > {
155166 final bool withFineDependencies;
156- final Uint32List _salt;
167+ final Uint32List saltForElements;
168+ final SourceFactory sourceFactory;
157169 final Map <LibraryFileKind , _LibraryNode > nodesOfFiles = {};
158170
159- _LibraryWalker (this .withFineDependencies, this ._salt);
171+ _LibraryWalker ({
172+ required this .withFineDependencies,
173+ required this .saltForElements,
174+ required this .sourceFactory,
175+ });
160176
161177 @override
162178 void evaluate (_LibraryNode v) {
@@ -166,7 +182,7 @@ class _LibraryWalker extends graph.DependencyWalker<_LibraryNode> {
166182 @override
167183 void evaluateScc (List <_LibraryNode > scc) {
168184 var apiSignature = ApiSignature ();
169- apiSignature.addUint32List (_salt );
185+ apiSignature.addUint32List (saltForElements );
170186
171187 // Sort libraries to produce stable signatures.
172188 scc.sort ((first, second) {
@@ -205,12 +221,23 @@ class _LibraryWalker extends graph.DependencyWalker<_LibraryNode> {
205221 }
206222 }
207223
224+ var transitivePackages = {
225+ ...directDependencies.expand (
226+ (dependency) => dependency.transitivePackages,
227+ ),
228+ ...libraries
229+ .map ((library) => library.file.uriProperties.packageName)
230+ .nonNulls,
231+ };
232+
208233 String manifestSignature;
209234 String nonTransitiveApiSignature;
210235 {
211236 var manifestBuilder = ApiSignature ();
212237 var apiSignatureBuilder = ApiSignature ();
213- manifestBuilder.addUint32List (_salt);
238+ manifestBuilder.addBytes (saltForElements);
239+ _addUriResolutionToSignature (manifestBuilder, transitivePackages);
240+
214241 var sortedFiles = libraries
215242 .expand ((library) => library.files)
216243 .sortedBy ((file) => file.path);
@@ -228,6 +255,7 @@ class _LibraryWalker extends graph.DependencyWalker<_LibraryNode> {
228255 withFineDependencies: withFineDependencies,
229256 libraries: libraries.toFixedList (),
230257 libraryUris: libraryUris,
258+ transitivePackages: transitivePackages,
231259 directDependencies: directDependencies,
232260 apiSignature: apiSignature.toHex (),
233261 manifestSignature: manifestSignature,
@@ -244,6 +272,55 @@ class _LibraryWalker extends graph.DependencyWalker<_LibraryNode> {
244272 return nodesOfFiles.putIfAbsent (file, () => _LibraryNode (this , file));
245273 }
246274
275+ /// Add URI resolution environment to [signature] .
276+ ///
277+ /// Library manifests are reused across analysis contexts. If two contexts
278+ /// resolve the same `package:` URI to different file system locations, the
279+ /// manifests **must not** be considered interchangeable - otherwise we can
280+ /// end up reusing a manifest built for a different package layout and get
281+ /// mismatched element IDs.
282+ ///
283+ /// To make the manifest key sensitive to the resolution environment (without
284+ /// over-invalidating), we:
285+ ///
286+ /// * include the SDK root path (when using a folder-based SDK), and
287+ /// * include the file system paths of the **transitively referenced**
288+ /// packages only - i.e. the packages in [packageNames] , not every package
289+ /// visible in the analysis context.
290+ ///
291+ /// This design strikes a balance:
292+ /// * Different package configurations that map a dependency to different
293+ /// locations produce different keys, forcing a rebuild where reuse would
294+ /// be detrimental to element ID stability.
295+ /// * Contexts whose *overall* resolution differs but that map the relevant
296+ /// packages identically can still reuse manifests.
297+ ///
298+ /// Trade-off: a dependency package (or the SDK) moving on disk changes the
299+ /// key for all cycles that depend on it, even if the package's *API* hasn't
300+ /// changed. This is intentional - package dependencies change rarely, but
301+ /// different package of the same (not well configured) workspace would
302+ /// constantly churn new element IDs for unfortunate shared library manifest.
303+ void _addUriResolutionToSignature (
304+ ApiSignature signature,
305+ Set <String > packageNames,
306+ ) {
307+ var sdk = sourceFactory.dartSdk;
308+ if (sdk is FolderBasedDartSdk ) {
309+ signature.addString (sdk.directory.path);
310+ }
311+
312+ var packageMap = sourceFactory.packageMap;
313+ if (packageMap != null ) {
314+ var packagePaths = packageNames
315+ .map ((packageName) => packageMap[packageName])
316+ .nonNulls
317+ .expand ((folders) => folders)
318+ .map ((folder) => folder.path)
319+ .sorted ();
320+ signature.addStringList (packagePaths);
321+ }
322+ }
323+
247324 void _appendDirectlyReferenced (
248325 Set <LibraryCycle > directDependencies,
249326 ApiSignature apiSignature,
0 commit comments