diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Snapshot/Filters/AddDependencyContext.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Snapshot/Filters/AddDependencyContext.cs index 9239ae746b4..5f4eba90fd2 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Snapshot/Filters/AddDependencyContext.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Snapshot/Filters/AddDependencyContext.cs @@ -59,9 +59,8 @@ public bool TryGetDependency(DependencyId dependencyId, out IDependency dependen /// public void AddOrUpdate(IDependency dependency) { - DependencyId key = dependency.GetDependencyId(); - _dependencyById.Remove(key); - _dependencyById.Add(key, dependency); + _dependencyById[dependency.GetDependencyId()] = dependency; + Changed = true; } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Snapshot/Filters/UnresolvedDependenciesSnapshotFilter.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Snapshot/Filters/UnresolvedDependenciesSnapshotFilter.cs deleted file mode 100644 index 1b7a0a16878..00000000000 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Snapshot/Filters/UnresolvedDependenciesSnapshotFilter.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information. - -using System.ComponentModel.Composition; - -namespace Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Snapshot.Filters -{ - /// - /// Prohibits the unresolved dependency rule (evaluation) from overriding the corresponding - /// resolved rule (design-time build) in the snapshot. - /// - /// - /// Once resolved, a dependency cannot revert to unresolved state. It will only appear as - /// unresolved again if it is first removed. - /// - [Export(typeof(IDependenciesSnapshotFilter))] - [AppliesTo(ProjectCapability.DependenciesTree)] - [Order(Order)] - internal sealed class UnresolvedDependenciesSnapshotFilter : DependenciesSnapshotFilterBase - { - public const int Order = 100; - - public override void BeforeAddOrUpdate( - IDependency dependency, - AddDependencyContext context) - { - // TODO should this verify that the existing one is actually resolved? - if (!dependency.Resolved && context.Contains(dependency.GetDependencyId())) - { - context.Reject(); - return; - } - - context.Accept(dependency); - } - } -} diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependenciesChangesBuilder.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependenciesChangesBuilder.cs index 09e30ace98a..1c43b0e7972 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependenciesChangesBuilder.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependenciesChangesBuilder.cs @@ -8,24 +8,45 @@ namespace Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Subscriptions { internal sealed class DependenciesChangesBuilder { + /// + /// Shared running set of resolved dependencies. + /// + /// + /// Used in , where the caller wants to ensure we don't regress a resolved item to + /// unresolved state when we receive an evaluation-only update. + /// + private readonly HashSet<(TargetFramework TargetFramework, string ProviderType, string DependencyId)>? _resolvedItems; + private HashSet? _added; private HashSet? _removed; - public void Added(IDependencyModel model) + public DependenciesChangesBuilder(HashSet<(TargetFramework TargetFramework, string ProviderType, string DependencyId)>? resolvedItems = null) + { + _resolvedItems = resolvedItems; + } + + public void Added(TargetFramework targetFramework, IDependencyModel model) { _added ??= new HashSet(IDependencyModelEqualityComparer.Instance); _added.Remove(model); _added.Add(model); + + if (model.Resolved) + { + _resolvedItems?.Add((targetFramework, model.ProviderType, model.Id)); + } } - public void Removed(string providerType, string dependencyId) + public void Removed(TargetFramework targetFramework, string providerType, string dependencyId) { _removed ??= new HashSet(IDependencyModelEqualityComparer.Instance); var identity = new RemovedDependencyModel(providerType, dependencyId); _removed.Remove(identity); _removed.Add(identity); + + _resolvedItems?.Remove((targetFramework, providerType, dependencyId)); } public IDependenciesChanges? TryBuildChanges() @@ -40,6 +61,11 @@ public void Removed(string providerType, string dependencyId) _removed == null ? (IImmutableList)ImmutableList.Empty : ImmutableArray.CreateRange(_removed)); } + public bool HasResolvedItem(TargetFramework targetFramework, string providerType, string dependencyId) + { + return _resolvedItems?.Contains((targetFramework, providerType, dependencyId)) == true; + } + public override string ToString() => ToString(_added, _removed); private sealed class DependenciesChanges : IDependenciesChanges diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependencyRulesSubscriber.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependencyRulesSubscriber.cs index a7d18d8fa66..d4e5484cb11 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependencyRulesSubscriber.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependencyRulesSubscriber.cs @@ -27,6 +27,8 @@ internal sealed class DependencyRulesSubscriber : DependencyRulesSubscriberBase< private readonly Lazy _watchedEvaluationRules; private readonly Lazy _watchedJointRules; + private readonly HashSet<(TargetFramework TargetFramework, string ProviderType, string DependencyId)> _resolvedItems = new(); + [ImportingConstructor] public DependencyRulesSubscriber( IUnconfiguredProjectCommonServices commonServices, @@ -117,15 +119,15 @@ protected override void Handle( } // Create an object to track dependency changes. - var changesBuilder = new DependenciesChangesBuilder(); + var changesBuilder = new DependenciesChangesBuilder(_resolvedItems); // Give each handler a chance to register dependency changes. foreach (Lazy handler in _handlers) { - IProjectChangeDescription evaluation = projectUpdate.ProjectChanges[handler.Value.EvaluatedRuleName]; - IProjectChangeDescription? build = projectUpdate.ProjectChanges.GetValueOrDefault(handler.Value.ResolvedRuleName); + IProjectChangeDescription evaluationProjectChange = projectUpdate.ProjectChanges[handler.Value.EvaluatedRuleName]; + IProjectChangeDescription? buildProjectChange = projectUpdate.ProjectChanges.GetValueOrDefault(handler.Value.ResolvedRuleName); - handler.Value.Handle(projectFullPath, evaluation, build, targetFrameworkToUpdate, changesBuilder); + handler.Value.Handle(projectFullPath, evaluationProjectChange, buildProjectChange, targetFrameworkToUpdate, changesBuilder); } IDependenciesChanges? changes = changesBuilder.TryBuildChanges(); diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependencySharedProjectsSubscriber.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependencySharedProjectsSubscriber.cs index 4947b89791a..84d852a3bcf 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependencySharedProjectsSubscriber.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/DependencySharedProjectsSubscriber.cs @@ -106,7 +106,7 @@ private void ProcessSharedProjectsUpdates( isResolved: true, isImplicit: false, properties: ImmutableStringDictionary.EmptyOrdinal); - changesBuilder.Added(added); + changesBuilder.Added(targetFramework, added); } // process removed nodes @@ -117,6 +117,7 @@ private void ProcessSharedProjectsUpdates( if (exists) { changesBuilder.Removed( + targetFramework, ProjectRuleHandler.ProviderTypeString, dependencyId: removedSharedImportPath); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/AnalyzerRuleHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/AnalyzerRuleHandler.cs index 24c4c9fa7cc..316cb46f1ba 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/AnalyzerRuleHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/AnalyzerRuleHandler.cs @@ -44,14 +44,14 @@ public AnalyzerRuleHandler() protected override IDependencyModel CreateDependencyModel( string path, string originalItemSpec, - bool resolved, + bool isResolved, bool isImplicit, IImmutableDictionary properties) { return new AnalyzerDependencyModel( path, originalItemSpec, - resolved, + isResolved, isImplicit, properties); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/AssemblyRuleHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/AssemblyRuleHandler.cs index b353f4595c3..b4afe61f10f 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/AssemblyRuleHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/AssemblyRuleHandler.cs @@ -42,14 +42,14 @@ public AssemblyRuleHandler() protected override IDependencyModel CreateDependencyModel( string path, string originalItemSpec, - bool resolved, + bool isResolved, bool isImplicit, IImmutableDictionary properties) { return new AssemblyDependencyModel( path, originalItemSpec, - resolved, + isResolved, isImplicit, properties); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/ComRuleHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/ComRuleHandler.cs index 9be75ec7e83..45ee109dd66 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/ComRuleHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/ComRuleHandler.cs @@ -42,14 +42,14 @@ public ComRuleHandler() protected override IDependencyModel CreateDependencyModel( string path, string originalItemSpec, - bool resolved, + bool isResolved, bool isImplicit, IImmutableDictionary properties) { return new ComDependencyModel( path, originalItemSpec, - resolved, + isResolved, isImplicit, properties); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/DependenciesRuleHandlerBase.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/DependenciesRuleHandlerBase.cs index 3b8f2824748..04c56973a72 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/DependenciesRuleHandlerBase.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/DependenciesRuleHandlerBase.cs @@ -54,43 +54,37 @@ public void Handle( TargetFramework targetFramework, DependenciesChangesBuilder changesBuilder) { - HandleChangesForRule( - resolved: false, - evaluationProjectChange, - null, - isEvaluatedItemSpec: null); + bool hasResolvedData = buildProjectChange is not null; + + HandleChangesForRule(evaluationProjectChange); // We only have resolved data if the update came via the JointRule data source. - if (buildProjectChange != null) + if (hasResolvedData) { Func? isEvaluatedItemSpec = ResolvedItemRequiresEvaluatedItem ? evaluationProjectChange.After.Items.ContainsKey : (Func?)null; - HandleChangesForRule( - resolved: true, - evaluationProjectChange, - buildProjectChange, - isEvaluatedItemSpec); + HandleChangesForRule(evaluationProjectChange, buildProjectChange, isEvaluatedItemSpec); } return; - void HandleChangesForRule(bool resolved, IProjectChangeDescription evaluationProjectChange, IProjectChangeDescription? buildProjectChange, Func? isEvaluatedItemSpec) + void HandleChangesForRule(IProjectChangeDescription evaluationProjectChange, IProjectChangeDescription? buildProjectChange = null, Func? isEvaluatedItemSpec = null) { - IProjectChangeDescription projectChange = resolved ? buildProjectChange! : evaluationProjectChange; + IProjectChangeDescription projectChange = buildProjectChange ?? evaluationProjectChange; foreach (string removedItem in projectChange.Difference.RemovedItems) { - HandleRemovedItem(projectFullPath, removedItem, resolved, projectChange, evaluationProjectChange.After, changesBuilder, targetFramework, isEvaluatedItemSpec); + HandleRemovedItem(projectFullPath, removedItem, projectChange, evaluationProjectChange.After, changesBuilder, targetFramework, isEvaluatedItemSpec); } foreach (string changedItem in projectChange.Difference.ChangedItems) { - HandleChangedItem(projectFullPath, changedItem, resolved, projectChange, evaluationProjectChange.After, changesBuilder, targetFramework, isEvaluatedItemSpec); + HandleAddedItem(projectFullPath, changedItem, projectChange, evaluationProjectChange.After, changesBuilder, targetFramework, isEvaluatedItemSpec); } foreach (string addedItem in projectChange.Difference.AddedItems) { - HandleAddedItem(projectFullPath, addedItem, resolved, projectChange, evaluationProjectChange.After, changesBuilder, targetFramework, isEvaluatedItemSpec); + HandleAddedItem(projectFullPath, addedItem, projectChange, evaluationProjectChange.After, changesBuilder, targetFramework, isEvaluatedItemSpec); } System.Diagnostics.Debug.Assert(projectChange.Difference.RenamedItems.Count == 0, "Project rule diff should not contain renamed items"); @@ -100,63 +94,54 @@ void HandleChangesForRule(bool resolved, IProjectChangeDescription evaluationPro protected virtual void HandleAddedItem( string projectFullPath, string addedItem, - bool resolved, IProjectChangeDescription projectChange, IProjectRuleSnapshot evaluationRuleSnapshot, DependenciesChangesBuilder changesBuilder, TargetFramework targetFramework, Func? isEvaluatedItemSpec) { - IDependencyModel? model = CreateDependencyModelForRule(addedItem, evaluationRuleSnapshot, projectChange.After, resolved, projectFullPath); + bool isResolved = isEvaluatedItemSpec is not null; + + IDependencyModel? model = CreateDependencyModelForRule(addedItem, evaluationRuleSnapshot, projectChange.After, isResolved, changesBuilder, targetFramework, projectFullPath); if (model != null && (isEvaluatedItemSpec == null || isEvaluatedItemSpec(model.Id))) { - changesBuilder.Added(model); + changesBuilder.Added(targetFramework, model); } } protected virtual void HandleRemovedItem( string projectFullPath, string removedItem, - bool resolved, IProjectChangeDescription projectChange, IProjectRuleSnapshot evaluationRuleSnapshot, DependenciesChangesBuilder changesBuilder, TargetFramework targetFramework, Func? isEvaluatedItemSpec) { - string dependencyId = resolved - ? projectChange.Before.GetProjectItemProperties(removedItem)!.GetStringProperty(ProjectItemMetadata.OriginalItemSpec) ?? removedItem - : removedItem; - - if (isEvaluatedItemSpec == null || isEvaluatedItemSpec(dependencyId)) + if (isEvaluatedItemSpec is not null) { - changesBuilder.Removed(ProviderType, removedItem); + // The item is resolved + string dependencyId = projectChange.Before.GetProjectItemProperties(removedItem)!.GetStringProperty(ProjectItemMetadata.OriginalItemSpec) ?? removedItem; + + if (!isEvaluatedItemSpec(dependencyId)) + { + // Skip any items returned by build that were not present in evaluation + return; + } } + + changesBuilder.Removed(targetFramework, ProviderType, removedItem); } - protected virtual void HandleChangedItem( - string projectFullPath, - string changedItem, - bool resolved, - IProjectChangeDescription projectChange, + private IDependencyModel? CreateDependencyModelForRule( + string itemSpec, IProjectRuleSnapshot evaluationRuleSnapshot, + IProjectRuleSnapshot updatedRuleSnapshot, + bool isResolved, DependenciesChangesBuilder changesBuilder, TargetFramework targetFramework, - Func? isEvaluatedItemSpec) - { - IDependencyModel? model = CreateDependencyModelForRule(changedItem, evaluationRuleSnapshot, projectChange.After, resolved, projectFullPath); - - if (model != null && (isEvaluatedItemSpec == null || isEvaluatedItemSpec(model.Id))) - { - // For changes we try to add new dependency. If it is a resolved dependency, it would just override - // old one with new properties. If it is unresolved dependency, it would be added only when there is - // no resolved version in the snapshot (due to UnresolvedDependenciesSnapshotFilter). - changesBuilder.Added(model); - } - } - - private IDependencyModel? CreateDependencyModelForRule(string itemSpec, IProjectRuleSnapshot evaluationRuleSnapshot, IProjectRuleSnapshot updatedRuleSnapshot, bool isResolved, string projectFullPath) + string projectFullPath) { IImmutableDictionary? properties = updatedRuleSnapshot.GetProjectItemProperties(itemSpec); @@ -181,6 +166,9 @@ protected virtual void HandleChangedItem( bool isImplicit = IsImplicit(projectFullPath, evaluationProperties); + // When we only have evaluation data, mark the dependency as resolved if we currently have a corresponding resolved item + isResolved = isResolved || changesBuilder.HasResolvedItem(targetFramework, ProviderType, originalItemSpec); + return CreateDependencyModel( itemSpec, originalItemSpec, @@ -218,7 +206,7 @@ protected static bool IsImplicit( protected virtual IDependencyModel CreateDependencyModel( string path, string originalItemSpec, - bool resolved, + bool isResolved, bool isImplicit, IImmutableDictionary properties) { diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/FrameworkRuleHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/FrameworkRuleHandler.cs index 77f15001e3f..6c3a799e559 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/FrameworkRuleHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/FrameworkRuleHandler.cs @@ -42,14 +42,14 @@ public FrameworkRuleHandler() protected override IDependencyModel CreateDependencyModel( string path, string originalItemSpec, - bool resolved, + bool isResolved, bool isImplicit, IImmutableDictionary properties) { return new FrameworkDependencyModel( path, originalItemSpec, - resolved, + isResolved, isImplicit, properties); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/PackageRuleHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/PackageRuleHandler.cs index 5900e5b2c88..f2f1fb0abfd 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/PackageRuleHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/PackageRuleHandler.cs @@ -47,7 +47,6 @@ public PackageRuleHandler(ITargetFrameworkProvider targetFrameworkProvider) protected override void HandleAddedItem( string projectFullPath, string addedItem, - bool resolved, IProjectChangeDescription projectChange, IProjectRuleSnapshot evaluationRuleSnapshot, DependenciesChangesBuilder changesBuilder, @@ -57,57 +56,33 @@ protected override void HandleAddedItem( if (TryCreatePackageDependencyModel( projectFullPath, addedItem, - resolved, properties: projectChange.After.GetProjectItemProperties(addedItem)!, evaluationRuleSnapshot, isEvaluatedItemSpec, targetFramework, + changesBuilder, out PackageDependencyModel? dependencyModel)) { - changesBuilder.Added(dependencyModel); - } - } - - protected override void HandleChangedItem( - string projectFullPath, - string changedItem, - bool resolved, - IProjectChangeDescription projectChange, - IProjectRuleSnapshot evaluationRuleSnapshot, - DependenciesChangesBuilder changesBuilder, - TargetFramework targetFramework, - Func? isEvaluatedItemSpec) - { - if (TryCreatePackageDependencyModel( - projectFullPath, - changedItem, - resolved, - properties: projectChange.After.GetProjectItemProperties(changedItem)!, - evaluationRuleSnapshot, - isEvaluatedItemSpec, - targetFramework, - out PackageDependencyModel? dependencyModel)) - { - changesBuilder.Removed(ProviderTypeString, dependencyModel.OriginalItemSpec); - changesBuilder.Added(dependencyModel); + changesBuilder.Added(targetFramework, dependencyModel); } } protected override void HandleRemovedItem( string projectFullPath, string removedItem, - bool resolved, IProjectChangeDescription projectChange, IProjectRuleSnapshot evaluationRuleSnapshot, DependenciesChangesBuilder changesBuilder, TargetFramework targetFramework, Func? isEvaluatedItemSpec) { - string originalItemSpec = resolved + bool isResolvedItem = isEvaluatedItemSpec is not null; + + string originalItemSpec = isResolvedItem ? projectChange.Before.GetProjectItemProperties(removedItem)?.GetStringProperty(ProjectItemMetadata.Name) ?? removedItem : removedItem; - changesBuilder.Removed(ProviderTypeString, originalItemSpec); + changesBuilder.Removed(targetFramework, ProviderTypeString, originalItemSpec); } public override IDependencyModel CreateRootDependencyNode() => s_groupModel; @@ -117,40 +92,24 @@ protected override void HandleRemovedItem( private bool TryCreatePackageDependencyModel( string projectFullPath, string itemSpec, - bool isResolved, IImmutableDictionary properties, IProjectRuleSnapshot evaluationRuleSnapshot, Func? isEvaluatedItemSpec, TargetFramework targetFramework, + DependenciesChangesBuilder changesBuilder, [NotNullWhen(returnValue: true)] out PackageDependencyModel? dependencyModel) { Requires.NotNullOrEmpty(itemSpec, nameof(itemSpec)); Requires.NotNull(properties, nameof(properties)); + Requires.NotNull(targetFramework, nameof(targetFramework)); - // PackageReference uses Name rather than OriginalItemSpec. - string originalItemSpec = isResolved - ? properties.GetStringProperty(ProjectItemMetadata.Name) ?? itemSpec - : itemSpec; + bool isResolvedItem = isEvaluatedItemSpec is not null; - IImmutableDictionary? evaluationProperties = evaluationRuleSnapshot.GetProjectItemProperties(originalItemSpec); + string originalItemSpec; - if (evaluationProperties == null) - { - // This package is present in build results, but not in evaluation. - // We disallow packages which are not present in evaluation. - dependencyModel = null; - return false; - } - - bool isImplicit = IsImplicit(projectFullPath, evaluationProperties); - - if (isResolved) + if (isEvaluatedItemSpec is not null) { // We have design-time build data - - Requires.NotNull(targetFramework, nameof(targetFramework)); - Requires.NotNull(isEvaluatedItemSpec!, nameof(isEvaluatedItemSpec)); - string? name = properties.GetStringProperty(ProjectItemMetadata.Name); string? dependencyType = properties.GetStringProperty(ProjectItemMetadata.Type); @@ -214,6 +173,7 @@ private bool TryCreatePackageDependencyModel( return false; } + // PackageReference uses Name rather than OriginalItemSpec. originalItemSpec = name; } else @@ -225,6 +185,21 @@ private bool TryCreatePackageDependencyModel( originalItemSpec = itemSpec; } + IImmutableDictionary? evaluationProperties = evaluationRuleSnapshot.GetProjectItemProperties(originalItemSpec); + + if (evaluationProperties == null) + { + // This package is present in build results, but not in evaluation. + // We disallow packages which are not present in evaluation. + dependencyModel = null; + return false; + } + + bool isImplicit = IsImplicit(projectFullPath, evaluationProperties); + + // When we only have evaluation data, mark the dependency as resolved if we currently have a corresponding resolved item + bool isResolved = isResolvedItem || changesBuilder.HasResolvedItem(targetFramework, ProviderType, originalItemSpec); + dependencyModel = new PackageDependencyModel( originalItemSpec, version: properties.GetStringProperty(ProjectItemMetadata.Version) ?? string.Empty, diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/ProjectRuleHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/ProjectRuleHandler.cs index 51cc959e937..3b54fc544d8 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/ProjectRuleHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/ProjectRuleHandler.cs @@ -42,14 +42,14 @@ public ProjectRuleHandler() protected override IDependencyModel CreateDependencyModel( string path, string originalItemSpec, - bool resolved, + bool isResolved, bool isImplicit, IImmutableDictionary properties) { return new ProjectDependencyModel( path, originalItemSpec, - resolved, + isResolved, isImplicit, properties); } diff --git a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/SdkRuleHandler.cs b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/SdkRuleHandler.cs index d8550384d77..844344a9df3 100644 --- a/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/SdkRuleHandler.cs +++ b/src/Microsoft.VisualStudio.ProjectSystem.Managed/ProjectSystem/Tree/Dependencies/Subscriptions/RuleHandlers/SdkRuleHandler.cs @@ -42,7 +42,7 @@ public SdkRuleHandler() protected override IDependencyModel CreateDependencyModel( string path, string originalItemSpec, - bool resolved, + bool isResolved, bool isImplicit, IImmutableDictionary properties) { @@ -53,7 +53,7 @@ protected override IDependencyModel CreateDependencyModel( return new SdkDependencyModel( path, originalItemSpec, - resolved && !isImplicit, + isResolved && !isImplicit, isImplicit, properties); } diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Dependencies/Snapshot/DependenciesSnapshotTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Dependencies/Snapshot/DependenciesSnapshotTests.cs index c43e384bc9c..24e51a46084 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Dependencies/Snapshot/DependenciesSnapshotTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Dependencies/Snapshot/DependenciesSnapshotTests.cs @@ -178,7 +178,7 @@ public void FromChanges_WithDependenciesChanges() ProviderType = "Xxx", Id = "dependency1" }; - targetChanges.Added(model); + targetChanges.Added(targetFramework, model); var snapshot = DependenciesSnapshot.FromChanges( previousSnapshot, diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Dependencies/Snapshot/TargetedDependenciesSnapshotTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Dependencies/Snapshot/TargetedDependenciesSnapshotTests.cs index ada180bed0d..e886078bbe8 100644 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Dependencies/Snapshot/TargetedDependenciesSnapshotTests.cs +++ b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Dependencies/Snapshot/TargetedDependenciesSnapshotTests.cs @@ -129,8 +129,8 @@ public void FromChanges_AddingToEmpty() }; var changes = new DependenciesChangesBuilder(); - changes.Added(resolved); - changes.Added(unresolved); + changes.Added(targetFramework, resolved); + changes.Added(targetFramework, unresolved); var snapshot = TargetedDependenciesSnapshot.FromChanges( previousSnapshot, @@ -180,7 +180,7 @@ public void FromChanges_NoChangesAfterBeforeAddFilterDeclinedChange() ImmutableArray.Create(dependency1)); var changes = new DependenciesChangesBuilder(); - changes.Added(dependencyModelNew1); + changes.Added(targetFramework, dependencyModelNew1); var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddReject("Xxx", "newdependency1"); @@ -240,7 +240,7 @@ public void FromChanges_ReportedChangesAfterBeforeAddFilterDeclinedChange() ImmutableArray.Create(dependency1, dependency2)); var changes = new DependenciesChangesBuilder(); - changes.Added(dependencyModelNew1); + changes.Added(targetFramework, dependencyModelNew1); var filterAddedDependency = new TestDependency { Id = "unexpected" }; @@ -354,10 +354,10 @@ public void FromChanges_RemovedAndAddedChanges() ImmutableArray.Create(dependency1, dependency2, dependencyRemoved1)); var changes = new DependenciesChangesBuilder(); - changes.Added(dependencyModelAdded1); - changes.Added(dependencyModelAdded2); - changes.Added(dependencyModelAdded3); - changes.Removed("Xxx", "Removeddependency1"); + changes.Added(targetFramework, dependencyModelAdded1); + changes.Added(targetFramework, dependencyModelAdded2); + changes.Added(targetFramework, dependencyModelAdded3); + changes.Removed(targetFramework, "Xxx", "Removeddependency1"); var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddReject("Xxx", "addeddependency1") @@ -427,7 +427,7 @@ public void FromChanges_UpdatesLevelDependencies() ImmutableArray.Create(dependencyPrevious)); var changes = new DependenciesChangesBuilder(); - changes.Added(dependencyModelAdded); + changes.Added(targetFramework, dependencyModelAdded); var snapshotFilter = new TestDependenciesSnapshotFilter() .BeforeAddAccept("Xxx", "dependency1", dependencyUpdated); @@ -470,7 +470,7 @@ public void FromChanges_DifferentModelIdCapitalisation() ImmutableArray.Create(dependencyPrevious)); var changes = new DependenciesChangesBuilder(); - changes.Added(dependencyModelUpdated); + changes.Added(targetFramework, dependencyModelUpdated); var snapshot = TargetedDependenciesSnapshot.FromChanges( previousSnapshot, diff --git a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Dependencies/Snapshot/UnresolvedDependenciesSnapshotFilterTests.cs b/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Dependencies/Snapshot/UnresolvedDependenciesSnapshotFilterTests.cs deleted file mode 100644 index 95d89aa6171..00000000000 --- a/tests/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/ProjectSystem/Tree/Dependencies/Snapshot/UnresolvedDependenciesSnapshotFilterTests.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information. - -using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Models; -using Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Snapshot.Filters; -using Xunit; - -namespace Microsoft.VisualStudio.ProjectSystem.Tree.Dependencies.Snapshot -{ - public sealed class UnresolvedDependenciesSnapshotFilterTests - { - [Fact] - public void BeforeAddOrUpdate_WhenUnresolvedAndExistsResolvedInSnapshot_ShouldReturnNull() - { - var unresolvedDependency = new TestDependency { Id = "dependency", Resolved = false }; - var resolvedDependency = new TestDependency { Id = "dependency", Resolved = true }; - - var dependencyById = new Dictionary - { - { resolvedDependency.GetDependencyId(), resolvedDependency } - }; - - var context = new AddDependencyContext(dependencyById); - - var filter = new UnresolvedDependenciesSnapshotFilter(); - - filter.BeforeAddOrUpdate( - unresolvedDependency, - context); - - // Dependency rejected - Assert.Null(context.GetResult(filter)); - - // Nothing else changed - Assert.False(context.Changed); - } - - [Fact] - public void BeforeAddOrUpdate_WhenUnresolvedAndNotExistsResolvedInSnapshot_ShouldReturnDependency() - { - var unresolvedDependency = new TestDependency { Id = "dependency", Resolved = false }; - - var dependencyById = new Dictionary(); - - var context = new AddDependencyContext(dependencyById); - - var filter = new UnresolvedDependenciesSnapshotFilter(); - - filter.BeforeAddOrUpdate( - unresolvedDependency, - context); - - // Dependency accepted unchanged - Assert.Same(unresolvedDependency, context.GetResult(filter)); - - // Nothing else changed - Assert.False(context.Changed); - } - - [Fact] - public void BeforeAddOrUpdate_WhenResolved_ShouldReturnDependency() - { - var resolvedDependency = new TestDependency { Id = "dependency", Resolved = true }; - - var dependencyById = new Dictionary(); - - var context = new AddDependencyContext(dependencyById); - - var filter = new UnresolvedDependenciesSnapshotFilter(); - - filter.BeforeAddOrUpdate( - resolvedDependency, - context); - - // Dependency accepted unchanged - Assert.Same(resolvedDependency, context.GetResult(filter)); - - // Nothing else changed - Assert.False(context.Changed); - } - } -}