Skip to content

Commit c8fba92

Browse files
Support adding package and reference assemblies from an import (#76337)
Fixes #8788. Looks like this: ![image](https://github.com/user-attachments/assets/1be1bedb-a2ff-4a28-b584-851970dcb47d) The basic idea was simple. remove the restriction that disabled the feature within a using/import. But also made it so we only search for packages/references in that context, using the same indices we already have.
2 parents aea1c90 + 2f1cfa9 commit c8fba92

27 files changed

+639
-569
lines changed

src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingNuGetTests.cs

Lines changed: 164 additions & 128 deletions
Large diffs are not rendered by default.

src/EditorFeatures/VisualBasicTest/CodeActions/AddImport/AddImportTests_NuGet.vb

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ Imports Microsoft.CodeAnalysis.AddImport
88
Imports Microsoft.CodeAnalysis.CodeActions
99
Imports Microsoft.CodeAnalysis.CodeFixes
1010
Imports Microsoft.CodeAnalysis.Diagnostics
11-
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
12-
Imports Microsoft.CodeAnalysis.Options
1311
Imports Microsoft.CodeAnalysis.Packaging
14-
Imports Microsoft.CodeAnalysis.Shared.Utilities
1512
Imports Microsoft.CodeAnalysis.SymbolSearch
1613
Imports Microsoft.CodeAnalysis.VisualBasic.AddImport
1714
Imports Moq
@@ -56,9 +53,9 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeActions.AddImp
5653
Returns(SpecializedTasks.True)
5754

5855
Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict)
59-
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))).
60-
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty))
61-
packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(PackageSourceHelper.NugetOrgSourceName, "NuGetType", 0, It.IsAny(Of CancellationToken)())).
56+
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesAsync(New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken))).
57+
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyResult).Empty))
58+
packageServiceMock.Setup(Function(s) s.FindPackagesAsync(PackageSourceHelper.NugetOrgSourceName, New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken)())).
6259
Returns(Function() CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NuGetNamespace")))
6360

6461
Await TestInRegularAndScriptAsync(
@@ -85,9 +82,9 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa
8582
Returns(SpecializedTasks.True)
8683

8784
Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict)
88-
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))).
89-
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty))
90-
packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(PackageSourceHelper.NugetOrgSourceName, "NuGetType", 0, It.IsAny(Of CancellationToken)())).
85+
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesAsync(New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken))).
86+
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyResult).Empty))
87+
packageServiceMock.Setup(Function(s) s.FindPackagesAsync(PackageSourceHelper.NugetOrgSourceName, New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken)())).
9188
Returns(Function() CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NS1", "NS2")))
9289

9390
Await TestInRegularAndScriptAsync(
@@ -114,9 +111,9 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa
114111
Returns(SpecializedTasks.False)
115112

116113
Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict)
117-
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))).
118-
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty))
119-
packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(PackageSourceHelper.NugetOrgSourceName, "NuGetType", 0, It.IsAny(Of CancellationToken)())).
114+
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesAsync(New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken))).
115+
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyResult).Empty))
116+
packageServiceMock.Setup(Function(s) s.FindPackagesAsync(PackageSourceHelper.NugetOrgSourceName, New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken)())).
120117
Returns(Function() CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NS1", "NS2")))
121118

122119
Await TestInRegularAndScriptAsync(
@@ -139,9 +136,9 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa
139136
Returns(True)
140137

141138
Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict)
142-
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))).
143-
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty))
144-
packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(PackageSourceHelper.NugetOrgSourceName, "NuGetType", 0, It.IsAny(Of CancellationToken)())).
139+
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesAsync(New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken))).
140+
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyResult).Empty))
141+
packageServiceMock.Setup(Function(s) s.FindPackagesAsync(PackageSourceHelper.NugetOrgSourceName, New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken)())).
145142
Returns(Function() CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NS1", "NS2")))
146143

147144
Await TestMissingInRegularAndScriptAsync(
@@ -164,9 +161,9 @@ New TestParameters(fixProviderData:=New ProviderData(installerServiceMock.Object
164161
Returns(ImmutableArray.Create("1.0", "2.0"))
165162

166163
Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict)
167-
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))).
168-
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty))
169-
packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(PackageSourceHelper.NugetOrgSourceName, "NuGetType", 0, It.IsAny(Of CancellationToken)())).
164+
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesAsync(New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken))).
165+
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyResult).Empty))
166+
packageServiceMock.Setup(Function(s) s.FindPackagesAsync(PackageSourceHelper.NugetOrgSourceName, New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken)())).
170167
Returns(Function() CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NS1", "NS2")))
171168

172169
Dim data = New ProviderData(installerServiceMock.Object, packageServiceMock.Object)
@@ -206,9 +203,9 @@ parameters:=New TestParameters(index:=2, fixProviderData:=data))
206203
Returns(SpecializedTasks.True)
207204

208205
Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict)
209-
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))).
210-
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty))
211-
packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(PackageSourceHelper.NugetOrgSourceName, "NuGetType", 0, It.IsAny(Of CancellationToken)())).
206+
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesAsync(New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken))).
207+
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyResult).Empty))
208+
packageServiceMock.Setup(Function(s) s.FindPackagesAsync(PackageSourceHelper.NugetOrgSourceName, New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken)())).
212209
Returns(Function() CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NuGetNamespace")))
213210

214211
Await TestInRegularAndScriptAsync(
@@ -238,9 +235,9 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa
238235
Returns(SpecializedTasks.True)
239236

240237
Dim packageServiceMock = New Mock(Of ISymbolSearchService)(MockBehavior.Strict)
241-
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesWithTypeAsync("NuGetType", 0, It.IsAny(Of CancellationToken))).
242-
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyWithTypeResult).Empty))
243-
packageServiceMock.Setup(Function(s) s.FindPackagesWithTypeAsync(PackageSourceHelper.NugetOrgSourceName, "NuGetType", 0, It.IsAny(Of CancellationToken)())).
238+
packageServiceMock.Setup(Function(s) s.FindReferenceAssembliesAsync(New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken))).
239+
Returns(Function() ValueTaskFactory.FromResult(ImmutableArray(Of ReferenceAssemblyResult).Empty))
240+
packageServiceMock.Setup(Function(s) s.FindPackagesAsync(PackageSourceHelper.NugetOrgSourceName, New TypeQuery("NuGetType", 0), It.IsAny(Of NamespaceQuery), It.IsAny(Of CancellationToken)())).
244241
Returns(Function() CreateSearchResult("NuGetPackage", "NuGetType", CreateNameParts("NuGetNamespace")))
245242

246243
Await TestInRegularAndScriptAsync(
@@ -257,16 +254,16 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa
257254
installerServiceMock.Verify()
258255
End Function
259256

260-
Private Shared Function CreateSearchResult(packageName As String, typeName As String, nameParts As ImmutableArray(Of String)) As ValueTask(Of ImmutableArray(Of PackageWithTypeResult))
261-
Return CreateSearchResult(New PackageWithTypeResult(
257+
Private Shared Function CreateSearchResult(packageName As String, typeName As String, nameParts As ImmutableArray(Of String)) As ValueTask(Of ImmutableArray(Of PackageResult))
258+
Return CreateSearchResult(New PackageResult(
262259
packageName:=packageName,
263260
rank:=0,
264261
typeName:=typeName,
265262
version:=Nothing,
266263
containingNamespaceNames:=nameParts))
267264
End Function
268265

269-
Private Shared Function CreateSearchResult(ParamArray results As PackageWithTypeResult()) As ValueTask(Of ImmutableArray(Of PackageWithTypeResult))
266+
Private Shared Function CreateSearchResult(ParamArray results As PackageResult()) As ValueTask(Of ImmutableArray(Of PackageResult))
270267
Return ValueTaskFactory.FromResult(ImmutableArray.Create(results))
271268
End Function
272269

src/Features/CSharp/Portable/AddImport/CSharpAddImportCodeFixProvider.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
#nullable disable
6-
75
using System.Collections.Immutable;
86
using System.Composition;
97
using System.Diagnostics.CodeAnalysis;
@@ -22,6 +20,11 @@ internal static class AddImportDiagnosticIds
2220
/// </summary>
2321
public const string CS0103 = nameof(CS0103);
2422

23+
/// <summary>
24+
/// The type or namespace name 'X' does not exist in the namespace 'Y' (are you missing an assembly reference?)
25+
/// </summary>
26+
public const string CS0234 = nameof(CS0234);
27+
2528
/// <summary>
2629
/// type or namespace could not be found
2730
/// </summary>
@@ -149,10 +152,10 @@ internal static class AddImportDiagnosticIds
149152
public const string CS8415 = nameof(CS8415);
150153

151154
public static ImmutableArray<string> FixableTypeIds =
152-
[CS0103, CS0246, CS0305, CS0308, CS0122, CS0307, CS0616, CS1580, CS1581, CS8129, IDEDiagnosticIds.UnboundIdentifierId];
155+
[CS0103, CS0234, CS0246, CS0305, CS0308, CS0122, CS0307, CS0616, CS1580, CS1581, CS8129, IDEDiagnosticIds.UnboundIdentifierId];
153156

154157
public static ImmutableArray<string> FixableDiagnosticIds =
155-
FixableTypeIds.Concat(ImmutableArray.Create(
158+
FixableTypeIds.Concat([
156159
CS1061,
157160
CS1935,
158161
CS1501,
@@ -168,11 +171,11 @@ internal static class AddImportDiagnosticIds
168171
CS1579,
169172
CS8414,
170173
CS8411,
171-
CS8415));
174+
CS8415]);
172175
}
173176

174177
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddImport), Shared]
175-
internal class CSharpAddImportCodeFixProvider : AbstractAddImportCodeFixProvider
178+
internal sealed class CSharpAddImportCodeFixProvider : AbstractAddImportCodeFixProvider
176179
{
177180
public override ImmutableArray<string> FixableDiagnosticIds => AddImportDiagnosticIds.FixableDiagnosticIds;
178181

src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,15 @@ namespace Microsoft.CodeAnalysis.CSharp.AddImport;
3232
using static SyntaxFactory;
3333

3434
[ExportLanguageService(typeof(IAddImportFeatureService), LanguageNames.CSharp), Shared]
35-
internal class CSharpAddImportFeatureService : AbstractAddImportFeatureService<SimpleNameSyntax>
35+
[method: ImportingConstructor]
36+
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
37+
internal sealed class CSharpAddImportFeatureService() : AbstractAddImportFeatureService<SimpleNameSyntax>
3638
{
37-
[ImportingConstructor]
38-
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
39-
public CSharpAddImportFeatureService()
40-
{
41-
}
39+
protected override bool IsWithinImport(SyntaxNode node)
40+
=> node.GetAncestor<UsingDirectiveSyntax>()?.Parent is CompilationUnitSyntax;
4241

4342
protected override bool CanAddImport(SyntaxNode node, bool allowInHiddenRegions, CancellationToken cancellationToken)
44-
{
45-
cancellationToken.ThrowIfCancellationRequested();
46-
return node.CanAddUsingDirectives(allowInHiddenRegions, cancellationToken);
47-
}
43+
=> node.CanAddUsingDirectives(allowInHiddenRegions, cancellationToken);
4844

4945
protected override bool CanAddImportForMethod(
5046
string diagnosticId, ISyntaxFacts syntaxFacts, SyntaxNode node, out SimpleNameSyntax nameNode)
@@ -162,7 +158,8 @@ protected override bool CanAddImportForQuery(string diagnosticId, SyntaxNode nod
162158
diagnosticId == CS1929) && // An extension method is in scope, but for another type
163159
node.AncestorsAndSelf().Any(n => n is QueryExpressionSyntax && !(n.Parent is QueryContinuationSyntax));
164160

165-
protected override bool CanAddImportForType(string diagnosticId, SyntaxNode node, out SimpleNameSyntax nameNode)
161+
protected override bool CanAddImportForTypeOrNamespace(
162+
string diagnosticId, SyntaxNode node, out SimpleNameSyntax nameNode)
166163
{
167164
nameNode = null;
168165
switch (diagnosticId)
@@ -190,6 +187,16 @@ protected override bool CanAddImportForType(string diagnosticId, SyntaxNode node
190187

191188
break;
192189

190+
case CS0234:
191+
// The type or namespace name 'X' does not exist in the namespace 'Y'.
192+
//
193+
// We support this within a using, on any part of the using name that doesn't bind.
194+
if (!this.IsWithinImport(node))
195+
return false;
196+
197+
nameNode = node as SimpleNameSyntax;
198+
return true;
199+
193200
default:
194201
return false;
195202
}

0 commit comments

Comments
 (0)