When we're loading dependencies, don't require exact versions#56432
Conversation
d66bbd2 to
3db6ee0
Compare
Roslyn has made some improvements to better handle the case where analyzers or generators have a mix of assembly versions, such as in microsoft/CsWin32#218. The Roslyn changes were made in dotnet/roslyn#56432, and this consumes it.
acbf323 to
1d06f5d
Compare
|
If I have a solution with two different projects, each with their own version of StyleCop.Analyzers installed, will the IDE still load both versions and match them with the corresponding project after this change? |
|
@sharwell, hmm, good point. Will add tests. |
…sis.CSharp.Workspaces dotnet/roslyn#56432
67120b6 to
eedcff7
Compare
eedcff7 to
15522e3
Compare
15522e3 to
879bb90
Compare
There's a bunch of similarly named methods here, this just clarifies one of them in particular.
Significantly changed, needs a re-review.
RikkiGibson
left a comment
There was a problem hiding this comment.
Change looks good to me! Thanks so much for the refactoring of desktop-specific stuff and for making the test fixture much more clear.
| } | ||
|
|
||
| var identity = AssemblyIdentityUtils.TryGetAssemblyIdentity(fullPath); | ||
| return AddToCache(fullPath, identity); |
There was a problem hiding this comment.
This is just going to call _loadedAssemblyIdentitiesByPath again and i'm not sure I see the purpose.
Consider that this implementation needs to decide if it requires the invariant that references do or do not change on disk. My assumption is that the invariant required here is references don't change on disk (otherwise there are other issues with the PR). If that is the case then why do we need to re-read from _loadedAsseblyIdentitiesByPath here? Sholudn't we just grab a lock and assign in?
There was a problem hiding this comment.
@jaredpar: this is unchanged from the current implementation:
I don't know why it's done that way either.
| _loadedAssembliesByPath[fullPath] = assembly; | ||
|
|
||
| return assembly; | ||
| _loadedAssembliesByPath[fullPath] = loadedAssembly; |
There was a problem hiding this comment.
Consider this sequence in the context of my other comment about invariants. There is no attempt to see if the state of the collection changed between lock calls, it's just a simple write operation.
We had a cache in AnalyzerAssemblyLoader that tried to cache and reuse the same assembly, keyed by assembly identity. This code makes some sense to keep around for the .NET full framework case where we can't load multiple assemblies with the same identity, but doesn't make sense to keep around for our AssemblyLoadContext implementation. The AssemblyLoadContext code correctly isolated dependencies and didn't use this cache when resolving dependencies, but it was still active when the public LoadFromPath entrypoint was called -- we'd hit the cache there before doing anything else. To ensure we don't have any identity-related caching logic reappearing in the AssemblyLoadContext implementation, the identity-related logic has been moved to DefaultAnalyzerAssemblyLoader.Desktop.cs, which greatly simplifies the base type.
ebb7048 to
7d8471a
Compare
If you had loading happening at the same time as a call to AddDependencyLocation, bad things might happen: - GetPaths wasn't taking a lock before looking at the dictionary - Code used the returned HashSet outside of the guarding lock
7d8471a to
00db8bd
Compare
When we're loading analyzer dependencies, our current logic was to require the exact version that was requested from the loading assembly. This is problematic in a lot of cases where you would normally have a binding redirect to ensure assemblies load cleanly in diamond dependency scenarios. Without this, if you depend on B.dll and A.dll, but B.dll depends on an older version of A.dll, you can't produce a package that actually loads because there's no sane way to package both A.dlls at the same time. We avoid adding any logic here where we'll still try to use an older version if that's still available. Consider a more complicated example of: 1. Analyzer 1 depends on and ships B.dll (version 2) and A.dll (version 2); B.dll (version 2) depends on A.dll (version 1). 2. Analyzer 2 depends on A.dll (version 1) and ships that. When loading Analyzer 1, we need a redirect to ensure the A that it consumes and the A that B consumes are unified. We don't want the presence of Analyzer 2 to change that. We could satisfy B.dll's request with Ananlyer 2's dependency, but if Analyzer 1 expects type unification then adding Analyzer 2 to the build would break it. Fixes dotnet#53672
a727f51 to
dca430c
Compare
Roslyn has made some improvements to better handle the case where analyzers or generators have a mix of assembly versions, such as in microsoft/CsWin32#218. The Roslyn changes were made in dotnet/roslyn#56432, and this consumes it.
Roslyn has made some improvements to better handle the case where analyzers or generators have a mix of assembly versions, such as in microsoft/CsWin32#218. The Roslyn changes were made in dotnet/roslyn#56432, and this consumes it.
Roslyn has made some improvements to better handle the case where analyzers or generators have a mix of assembly versions, such as in microsoft/CsWin32#218. The Roslyn changes were made in dotnet/roslyn#56432, and this consumes it.
When we're loading analyzer dependencies, our current logic was to require the exact version that was requested from the loading assembly. This is problematic in a lot of cases where you would normally have a binding redirect to ensure assemblies load cleanly; by requiring exact versions we'd end up in diamond dependency cases where you couldn't practically produce an analyzer package that would cleanly load.
While doing this change, @RikkiGibson and I noticed several other problems:
REVIEWING COMMIT-AT-A-TIME IS HIGHLY RECOMMENDED, since those changes are broken out to separate commits.
Fixes #53672
Fixes #56216