Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add codefixer and completion provider to install OpenAPI package from extension methods #55963

Merged
merged 10 commits into from
Jun 14, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class DiagnosticProject
private static readonly ICompilationAssemblyResolver _assemblyResolver = new AppBaseCompilationAssemblyResolver();
private static readonly Dictionary<Assembly, Solution> _solutionCache = new Dictionary<Assembly, Solution>();

public static Project Create(Assembly testAssembly, string[] sources, Func<Workspace> workspaceFactory = null, Type analyzerReference = null)
public static Project Create(Assembly testAssembly, string[] sources, Func<Workspace> workspaceFactory = null, Type[] analyzerReferences = null)
{
Solution solution;
lock (_solutionCache)
Expand All @@ -50,11 +50,14 @@ public static Project Create(Assembly testAssembly, string[] sources, Func<Works
}
}

if (analyzerReference != null)
if (analyzerReferences != null)
{
solution = solution.AddAnalyzerReference(
projectId,
new AnalyzerFileReference(analyzerReference.Assembly.Location, AssemblyLoader.Instance));
foreach (var analyzerReference in analyzerReferences)
{
solution = solution.AddAnalyzerReference(
projectId,
new AnalyzerFileReference(analyzerReference.Assembly.Location, AssemblyLoader.Instance));
}
}

_solutionCache.Add(testAssembly, solution);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,7 @@
}

var wellKnownTypes = WellKnownTypes.GetOrCreate(semanticModel.Compilation);

Dictionary<ThisAndExtensionMethod, PackageSourceAndNamespace> _wellKnownExtensionMethodCache = new()
{
{
new(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_Extensions_DependencyInjection_IServiceCollection), "AddOpenApi"),
new("Microsoft.AspNetCore.OpenApi", "Microsoft.Extensions.DependencyInjection")
},
{
new(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Builder_WebApplication), "MapOpenApi"),
new("Microsoft.AspNetCore.OpenApi", "Microsoft.AspNetCore.Builder")
}
};
var wellKnownExtensionMethodCache = ExtensionMethodsClass.ConstructFromWellKnownTypes(wellKnownTypes);

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: macOS arm64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl ARM64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl ARM)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux ARM)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux x64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl x64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux ARM64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: macOS x64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: macOS)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: Ubuntu x64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: Ubuntu x64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: macOS)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 36 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs#L36

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/AddPackageFixer.cs(36,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

foreach (var diagnostic in context.Diagnostics)
captainsafia marked this conversation as resolved.
Show resolved Hide resolved
{
Expand Down Expand Up @@ -80,7 +69,7 @@
}

var targetThisAndExtensionMethod = new ThisAndExtensionMethod(symbolType, methodName);
if (_wellKnownExtensionMethodCache.TryGetValue(targetThisAndExtensionMethod, out var packageSourceAndNamespace))
if (wellKnownExtensionMethodCache.TryGetValue(targetThisAndExtensionMethod, out var packageSourceAndNamespace))
{
var position = diagnostic.Location.SourceSpan.Start;
var packageInstallData = new AspNetCoreInstallPackageData(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using Microsoft.AspNetCore.Analyzers;
using Microsoft.AspNetCore.App.Analyzers.Infrastructure;

internal static class ExtensionMethodsCache
{
public static Dictionary<ThisAndExtensionMethod, PackageSourceAndNamespace> ConstructFromWellKnownTypes(WellKnownTypes wellKnownTypes)
{
return new()
{
{
new(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_Extensions_DependencyInjection_IServiceCollection), "AddOpenApi"),
new("Microsoft.AspNetCore.OpenApi", "Microsoft.Extensions.DependencyInjection")
},
{
new(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.Microsoft_AspNetCore_Builder_WebApplication), "MapOpenApi"),
new("Microsoft.AspNetCore.OpenApi", "Microsoft.AspNetCore.Builder")
}
};
}
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JamesNK Can I get your review on this given your experience implementing completion providers in route tooling?

The goal is to add completions for extension methods defined in a separate package then provide an installation codefix for them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been a minute since my head was in The Roslyn Zone. I'll do my best.

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.App.Analyzers.Infrastructure;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage;

[ExportCompletionProvider(nameof(ExtensionMethodsCompletionProvider), LanguageNames.CSharp)]
[Shared]
public sealed class ExtensionMethodsCompletionProvider : CompletionProvider
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment summarizing what the provider's deal is.

Does it replace completion for known types or are values from the completion provider added to the auto-complete? Replacing auto-complete for some types (if that's how it works) feels pretty invasive.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The completion provider is additive (assuming I understand the intent of the context.AddItem method 😅 ) and is intended to augment the completions list with methods that we think might be helpful. I'll add a comment to this effect.

Copy link
Member

@JamesNK JamesNK Jun 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the value is already present in auto-complete? For example, someone might already have a reference to the OpenAPI package. Do they see the AddOpenApi option from built-in autocomplete and a item from this provider?

Do items added by this provider look the same or different to other items? Could I see a screenshot?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So excited! 🥳

{
public override async Task ProvideCompletionsAsync(CompletionContext context)
{
if (!context.Document.SupportsSemanticModel)
{
return;
}

var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
if (root == null)
{
return;
}

var span = context.CompletionListSpan;
var token = root.FindToken(span.Start);
if (token.Parent == null)
{
return;
}

var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
if (semanticModel == null)
{
return;
}

var wellKnownTypes = WellKnownTypes.GetOrCreate(semanticModel.Compilation);
var wellKnownExtensionMethodCache = ExtensionMethodsClass.ConstructFromWellKnownTypes(wellKnownTypes);

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: macOS arm64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl ARM64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl ARM)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux ARM)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux x64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux Musl x64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: Linux ARM64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Build: macOS x64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: macOS)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr (Tests: Ubuntu x64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: Ubuntu x64)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci (Build Test: macOS)

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-quarantined-pr

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context

Check failure on line 47 in src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs

View check run for this annotation

Azure Pipelines / aspnetcore-ci

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs#L47

src/Framework/AspNetCoreAnalyzers/src/CodeFixes/Dependencies/ExtensionMethodsCompletionProvider.cs(47,45): error CS0103: (NETCORE_ENGINEERING_TELEMETRY=Build) The name 'ExtensionMethodsClass' does not exist in the current context
var nearestMemberAccessExpression = FindNearestMemberAccessExpression(token.Parent);

if (nearestMemberAccessExpression is not null && nearestMemberAccessExpression is MemberAccessExpressionSyntax memberAccess)
{
var symbol = semanticModel.GetSymbolInfo(memberAccess.Expression);
var symbolType = symbol.Symbol switch
{
IMethodSymbol methodSymbol => methodSymbol.ReturnType,
IPropertySymbol propertySymbol => propertySymbol.Type,
ILocalSymbol localSymbol => localSymbol.Type,
_ => null
};

var matchingExtensionMethods = wellKnownExtensionMethodCache.Where(pair => IsMatchingExtensionMethod(pair, symbolType, token));
foreach (var item in matchingExtensionMethods)
{
context.CompletionListSpan = span;
context.AddItem(CompletionItem.Create(
displayText: item.Key.ExtensionMethod,
sortText: item.Key.ExtensionMethod,
filterText: item.Key.ExtensionMethod
));
}
}
}

private static SyntaxNode? FindNearestMemberAccessExpression(SyntaxNode? node)
{
var current = node;
while (current != null)
{
if (current?.IsKind(SyntaxKind.SimpleMemberAccessExpression) ?? false)
{
return current;
}

current = current?.Parent;
}

return null;
}

private static bool IsMatchingExtensionMethod(
KeyValuePair<ThisAndExtensionMethod, PackageSourceAndNamespace> pair,
ISymbol? symbolType,
SyntaxToken token)
{
if (symbolType is null)
{
return false;
}

var isIdentifierToken = token.IsKind(SyntaxKind.IdentifierName) || token.IsKind(SyntaxKind.IdentifierToken);
return SymbolEqualityComparer.Default.Equals(pair.Key.ThisType, symbolType) &&
(!isIdentifierToken || pair.Key.ExtensionMethod.Contains(token.ValueText));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure;
using Microsoft.AspNetCore.Analyzers.WebApplicationBuilder;
using Microsoft.CodeAnalysis.Completion;

namespace Microsoft.AspNetCore.Analyzers.Dependencies;

public partial class ExtensionMethodsCompletionProviderTests
{
private TestDiagnosticAnalyzerRunner Runner { get; } = new(new WebApplicationBuilderAnalyzer());

public static object[][] CompletionTriggers =>
[
[CompletionTrigger.Invoke],
[null]
];

[Theory]
[MemberData(nameof(CompletionTriggers))]
public async Task ProvidesAddOpenApiCompletion(CompletionTrigger trigger)
{
// Arrange & Act
var result = await GetCompletionsAndServiceAsync(@"
using Microsoft.AspNetCore.Builder;

class Program
{
static void Main()
{
var builder = WebApplication.CreateBuilder();
builder.Services.$$
}
}
", trigger);

// Assert
Assert.True(result.ShouldTriggerCompletion);
Assert.Contains(result.Completions.ItemsList, item => item.DisplayText == "AddOpenApi");
}

[Theory]
[MemberData(nameof(CompletionTriggers))]
public async Task ProvidesAddOpenApiCompletionWithPartialToken(CompletionTrigger trigger)
{
// Arrange & Act
var result = await GetCompletionsAndServiceAsync(@"
using Microsoft.AspNetCore.Builder;

class Program
{
static void Main()
{
var builder = WebApplication.CreateBuilder();
builder.Services.[|Ad$$|]
}
}
", trigger);

// Assert
Assert.True(result.ShouldTriggerCompletion);
Assert.Contains(result.Completions.ItemsList, item => item.DisplayText == "AddOpenApi");
}

[Theory]
[MemberData(nameof(CompletionTriggers))]
public async Task DoesNotProvideCompletionIfNoStringMatchForServices(CompletionTrigger trigger)
{
// Arrange & Act
var result = await GetCompletionsAndServiceAsync(@"
using Microsoft.AspNetCore.Builder;

class Program
{
static void Main()
{
var builder = WebApplication.CreateBuilder();
builder.Services.[|Confi$$|]
}
}
", trigger);

// Assert
Assert.True(result.ShouldTriggerCompletion);
Assert.DoesNotContain(result.Completions.ItemsList, item => item.DisplayText == "AddOpenApi");
}

[Theory]
[MemberData(nameof(CompletionTriggers))]
public async Task ProvidesMapOpenApiCompletion(CompletionTrigger trigger)
{
// Arrange & Act
var result = await GetCompletionsAndServiceAsync(@"
using Microsoft.AspNetCore.Builder;

class Program
{
static void Main()
{
var app = WebApplication.Create();
app.$$
}
}
", trigger);

// Assert
Assert.True(result.ShouldTriggerCompletion);
Assert.Contains(result.Completions.ItemsList, item => item.DisplayText == "MapOpenApi");
}

[Theory]
[MemberData(nameof(CompletionTriggers))]
public async Task ProvidesMapOpenApiCompletionWithPartialToken(CompletionTrigger trigger)
{
// Arrange & Act
var result = await GetCompletionsAndServiceAsync(@"
using Microsoft.AspNetCore.Builder;

class Program
{
static void Main()
{
var app = WebApplication.Create();
app.[|Ma$$|]
}
}
", trigger);

// Assert
Assert.True(result.ShouldTriggerCompletion);
Assert.Contains(result.Completions.ItemsList, item => item.DisplayText == "MapOpenApi");
}

[Theory]
[MemberData(nameof(CompletionTriggers))]
public async Task DoesNotProvideCompletionIfNoStringMatchForWebApplication(CompletionTrigger trigger)
{
// Arrange & Act
var result = await GetCompletionsAndServiceAsync(@"
using Microsoft.AspNetCore.Builder;

class Program
{
static void Main()
{
var app = WebApplication.Create();
app.[|Use$$|]
}
}
", trigger);

// Assert
Assert.True(result.ShouldTriggerCompletion);
Assert.DoesNotContain(result.Completions.ItemsList, item => item.DisplayText == "MapOpenApi");
}

private Task<CompletionResult> GetCompletionsAndServiceAsync(string source, CompletionTrigger? completionTrigger = null)
{
return CompletionTestHelpers.GetCompletionsAndServiceAsync(Runner, source, completionTrigger);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
<Reference Include="Microsoft.AspNetCore.Mvc" />
<Reference Include="Microsoft.AspNetCore.Http.Results" />
<Reference Include="Microsoft.AspNetCore.RateLimiting" />
<Reference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" PrivateAssets="All" />
<Reference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" />
<Reference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ public static Project CreateProjectWithReferencesInBinDir(Assembly testAssembly,

Func<Workspace> createWorkspace = CreateWorkspace;

var project = DiagnosticProject.Create(testAssembly, source, createWorkspace, typeof(RoutePatternClassifier));
var project = DiagnosticProject.Create(testAssembly, source, createWorkspace, [typeof(RoutePatternClassifier), typeof(ExtensionMethodsCompletionProvider)]);
foreach (var assembly in Directory.EnumerateFiles(AppContext.BaseDirectory, "*.dll"))
{
if (!project.MetadataReferences.Any(c => string.Equals(Path.GetFileNameWithoutExtension(c.Display), Path.GetFileNameWithoutExtension(assembly), StringComparison.OrdinalIgnoreCase)))
Expand Down
Loading