diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj
index 4cbf8dc59eeb190a77683ca46938c0632334ca79..cf34d675ba756ad0d46cfa720ea6c62b40e42b63 100644
--- a/src/EditorFeatures/Core/EditorFeatures.csproj
+++ b/src/EditorFeatures/Core/EditorFeatures.csproj
@@ -283,6 +283,9 @@
+
+
+
diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs
similarity index 100%
rename from src/Features/Core/Portable/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs
rename to src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs
diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs
similarity index 96%
rename from src/Features/Core/Portable/CodeFixes/CodeFixService.cs
rename to src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs
index 4e1f69487da7a8a6fd5e0aaadc4b8e21aebbda30..20112578561ccbf4c7cf6f56e301567d0f32c2f3 100644
--- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs
+++ b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs
@@ -22,11 +22,12 @@
namespace Microsoft.CodeAnalysis.CodeFixes
{
+ using Editor.Shared.Utilities;
using DiagnosticId = String;
using LanguageKind = String;
[Export(typeof(ICodeFixService)), Shared]
- internal partial class CodeFixService : ICodeFixService
+ internal partial class CodeFixService : ForegroundThreadAffinitizedObject, ICodeFixService
{
private readonly IDiagnosticAnalyzerService _diagnosticService;
@@ -52,6 +53,7 @@ internal partial class CodeFixService : ICodeFixService
[ImportMany]IEnumerable> loggers,
[ImportMany]IEnumerable> fixers,
[ImportMany]IEnumerable> suppressionProviders)
+ : base(assertIsForeground: false)
{
_errorLoggers = loggers;
_diagnosticService = service;
@@ -407,12 +409,28 @@ private async Task ContainsAnyFix(Document document, DiagnosticData diagno
foreach (var fixer in allFixers)
{
await extensionManager.PerformActionAsync(fixer, () => fixer.RegisterCodeFixesAsync(context) ?? SpecializedTasks.EmptyTask).ConfigureAwait(false);
- if (!fixes.Any())
+ foreach (var fix in fixes)
{
- continue;
- }
+ if (!fix.Action.PerformFinalApplicabilityCheck)
+ {
+ return true;
+ }
- return true;
+ // Have to see if this fix is still applicable. Jump to the foreground thread
+ // to make that check.
+ var applicable = await Task.Factory.StartNew(() =>
+ {
+ this.AssertIsForeground();
+ return fix.Action.IsApplicable(document.Project.Solution.Workspace);
+ },
+ cancellationToken, TaskCreationOptions.None, this.ForegroundTaskScheduler).ConfigureAwait(false);
+ this.AssertIsBackground();
+
+ if (applicable)
+ {
+ return true;
+ }
+ }
}
return false;
diff --git a/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs b/src/EditorFeatures/Core/Implementation/CodeFixes/ICodeFixService.cs
similarity index 100%
rename from src/Features/Core/Portable/CodeFixes/ICodeFixService.cs
rename to src/EditorFeatures/Core/Implementation/CodeFixes/ICodeFixService.cs
diff --git a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReference.cs b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReference.cs
index 293bfde93cc9b3325b4d8f310b6a1f40ba5fc9eb..44314dff3d479253e5ce2d9df40f099885d6954f 100644
--- a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReference.cs
+++ b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReference.cs
@@ -51,6 +51,11 @@ public override int GetHashCode()
public abstract Glyph? GetGlyph(Document document);
public abstract Solution UpdateSolution(Document newDocument);
+
+ internal virtual Func GetIsApplicableCheck(Project contextProject)
+ {
+ return null;
+ }
}
private class ProjectSymbolReference : SymbolReference
@@ -86,6 +91,17 @@ public override Solution UpdateSolution(Document newDocument)
return newProject.Solution;
}
+
+ internal override Func GetIsApplicableCheck(Project contextProject)
+ {
+ if (contextProject.Id == _project.Id)
+ {
+ // no need to do applicability check for a reference in our own project.
+ return null;
+ }
+
+ return workspace => workspace.CanAddProjectReference(contextProject.Id, _project.Id);
+ }
}
private class MetadataSymbolReference : SymbolReference
diff --git a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs
index 88645bf932f65275a7b5cc64fe8ca9c6a50ec295..9bf4ca92168aeb8e36b8de0685e0979c92ed2e23 100644
--- a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs
+++ b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs
@@ -108,7 +108,8 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
if (description != null)
{
var action = new MyCodeAction(description, reference.GetGlyph(document),
- c => this.AddImportAndReferenceAsync(node, reference, document, placeSystemNamespaceFirst, c));
+ c => this.AddImportAndReferenceAsync(node, reference, document, placeSystemNamespaceFirst, c),
+ reference.GetIsApplicableCheck(document.Project));
context.RegisterCodeFix(action, diagnostic);
}
}
@@ -391,14 +392,27 @@ private static bool NotNull(SymbolReference reference)
private class MyCodeAction : CodeAction.SolutionChangeAction
{
private readonly Glyph? _glyph;
+ private readonly Func _isApplicable;
- public MyCodeAction(string title, Glyph? glyph, Func> createChangedSolution) :
+ public MyCodeAction(
+ string title,
+ Glyph? glyph,
+ Func> createChangedSolution,
+ Func isApplicable = null) :
base(title, createChangedSolution, equivalenceKey: title)
{
_glyph = glyph;
+ _isApplicable = isApplicable;
}
internal override int? Glyph => _glyph.HasValue ? (int)_glyph.Value : (int?)null;
+
+ internal override bool PerformFinalApplicabilityCheck => _isApplicable != null;
+
+ internal override bool IsApplicable(Workspace workspace)
+ {
+ return _isApplicable == null ? true : _isApplicable(workspace);
+ }
}
private struct SearchResult where T : ISymbol
diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj
index dd7d734a17a2739e31c549c8fddfd35182704160..92265236f82bd8edd6ed7c96a4f0f69c5dad1b68 100644
--- a/src/Features/Core/Portable/Features.csproj
+++ b/src/Features/Core/Portable/Features.csproj
@@ -105,13 +105,10 @@
-
-
-
diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs
index e2ba9c716a4f8ee4cb484d6a1115b57d99d8cb44..6468954f0c8a687ab3eb1903313d6b72c3dad4e4 100644
--- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs
+++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs
@@ -1047,6 +1047,66 @@ internal void OnAdditionalDocumentTextUpdatedOnDisk(DocumentId documentId)
return this.ServiceProvider.GetService(typeof(TService)) as TInterface;
}
+ internal override bool CanAddProjectReference(ProjectId referencingProject, ProjectId referencedProject)
+ {
+ _foregroundObject.AssertIsForeground();
+
+ IVsHierarchy referencingHierarchy;
+ IVsHierarchy referencedHierarchy;
+ if (!TryGetHierarchy(referencingProject, out referencingHierarchy) ||
+ !TryGetHierarchy(referencedProject, out referencedHierarchy))
+ {
+ return false;
+ }
+
+ var referencingProjectFlavor3 = referencingHierarchy as IVsProjectFlavorReferences3;
+ var referencedProjectFlavor3 = referencedHierarchy as IVsProjectFlavorReferences3;
+
+ if (referencingProjectFlavor3 != null && referencedProjectFlavor3 != null)
+ {
+ const int ContextFlags = (int)__VSQUERYFLAVORREFERENCESCONTEXT.VSQUERYFLAVORREFERENCESCONTEXT_RefreshReference;
+
+ uint canAddProjectReference;
+ uint canBeReferenced;
+ string unused;
+ if (ErrorHandler.Failed(referencingProjectFlavor3.QueryAddProjectReferenceEx(referencedHierarchy, ContextFlags, out canAddProjectReference, out unused)) ||
+ ErrorHandler.Failed(referencedProjectFlavor3.QueryCanBeReferencedEx(referencingHierarchy, ContextFlags, out canBeReferenced, out unused)))
+ {
+ // Something went wrong even trying to see if the reference would be allowed.
+ // Assume it won't be allowed.
+ return false;
+ }
+
+ if (canAddProjectReference == (uint)__VSREFERENCEQUERYRESULT.REFERENCE_DENY ||
+ canBeReferenced == (uint)__VSREFERENCEQUERYRESULT.REFERENCE_DENY)
+ {
+ // If either of them deny then add a project reference is not allowed.
+ return false;
+ }
+
+ if (canAddProjectReference == (int)__VSREFERENCEQUERYRESULT.REFERENCE_ALLOW ||
+ canBeReferenced == (int)__VSREFERENCEQUERYRESULT.REFERENCE_ALLOW)
+ {
+ // At least one allows this, and neither deny. So this is allowed.
+ return true;
+ }
+
+ // Both are unknown if they should allow this. Fall through and use the regular
+ // matrix check.
+ }
+
+ // Normal matrix check for projects that don't implement IVsProjectFlavorReferences3.
+ var referenceManager = GetVsService();
+ if (referenceManager == null)
+ {
+ return false;
+ }
+
+ var result = referenceManager.QueryCanReferenceProject(referencingHierarchy, referencedHierarchy);
+ return result == (uint)__VSREFERENCEQUERYRESULT.REFERENCE_ALLOW ||
+ result == (uint)__VSREFERENCEQUERYRESULT.REFERENCE_UNKNOWN;
+ }
+
///
/// A trivial implementation of that just
/// forwards the calls down to the underlying Workspace.
diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs
index 25daebcd42633ba53221167fc6d8564d39d735e6..1b1962158dea9a14af077a53f418bc788934cb48 100644
--- a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs
+++ b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs
@@ -255,6 +255,8 @@ protected virtual async Task PostProcessChangesAsync(Document document
return document;
}
+ internal virtual bool PerformFinalApplicabilityCheck => false;
+
///
/// Called by the CodeActions on the UI thread to determine if the CodeAction is still
/// applicable and should be presented to the user. CodeActions can override this if they
diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs
index 5895e2c8f2d50ccaa06bc61fa07209420d1bf1ee..1e16e31ecf396f5d6d42b908b06c792763f66981 100644
--- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs
+++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs
@@ -878,6 +878,15 @@ public virtual bool CanApplyChange(ApplyChangesKind feature)
return false;
}
+ ///
+ /// Returns true
if a reference to referencedProject can be added to
+ /// referencingProject. false
otherwise.
+ ///
+ internal virtual bool CanAddProjectReference(ProjectId referencingProject, ProjectId referencedProject)
+ {
+ return false;
+ }
+
///
/// Apply changes made to a solution back to the workspace.
///