Files
Misaki.HighPerformance/Misaki.HighPerformance.Analyzer/Misaki.HighPerformance.Analyzer.CodeFixes/StructCopyCodeFixProvider.cs
2026-02-01 01:56:17 +09:00

125 lines
5.7 KiB
C#

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Misaki.HighPerformance.Analyzer
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(StructCopyCodeFixProvider)), Shared]
public class StructCopyCodeFixProvider : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds
{
get
{
return ImmutableArray.Create(StructCopyCodeAnalyzer.DIAGNOSTIC_ID);
}
}
public sealed override FixAllProvider GetFixAllProvider()
{
// See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
return WellKnownFixAllProviders.BatchFixer;
}
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
// Find the expression identified by the diagnostic.
// This will be the Right-Hand Side (RHS) of the assignment or declaration
var expressionSyntax = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf()
.OfType<ExpressionSyntax>()
.First();
context.RegisterCodeFix(
CodeAction.Create(
"Share memory",
c => ShareAsync(context.Document, expressionSyntax, c),
"StructCopyCodeFixProvider_ShareMemory"),
diagnostic);
context.RegisterCodeFix(
CodeAction.Create(
"Transfer ownership",
c => TransferOwnershipAsync(context.Document, expressionSyntax, c),
"StructCopyCodeFixProvider_TransferOwnership"),
diagnostic);
}
private async Task<Document> ShareAsync(Document document, ExpressionSyntax expressionToFix, CancellationToken cancellationToken)
{
// 1. Get the semantic model to figure out exactly what type we are dealing with
var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
var typeSymbol = semanticModel.GetTypeInfo(expressionToFix).Type;
// 2. Generate the type name string (e.g., "UniquePtr<ID3D12Device>")
// We use ToMinimalDisplayString so Roslyn handles namespaces/using directives automatically.
var typeName = typeSymbol.ToMinimalDisplayString(semanticModel, expressionToFix.SpanStart);
var typeSyntax = SyntaxFactory.ParseTypeName(typeName);
// 3. Create the "Share()" invocation: expression.Share()
var detachMethod = SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
expressionToFix, // The 'a' in 'b = a'
SyntaxFactory.IdentifierName("Share")
);
var detachInvocation = SyntaxFactory.InvocationExpression(detachMethod);
// 4. Replace the old node with the new node in the Syntax Tree
var root = await document.GetSyntaxRootAsync(cancellationToken);
var newRoot = root.ReplaceNode(expressionToFix, detachInvocation);
return document.WithSyntaxRoot(newRoot);
}
private async Task<Document> TransferOwnershipAsync(Document document, ExpressionSyntax expressionToFix, CancellationToken cancellationToken)
{
// 1. Get the semantic model to figure out exactly what type we are dealing with
var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
var typeSymbol = semanticModel.GetTypeInfo(expressionToFix).Type;
// 2. Generate the type name string (e.g., "UniquePtr<ID3D12Device>")
// We use ToMinimalDisplayString so Roslyn handles namespaces/using directives automatically.
var typeName = typeSymbol.ToMinimalDisplayString(semanticModel, expressionToFix.SpanStart);
var typeSyntax = SyntaxFactory.ParseTypeName(typeName);
// 3. Create the "Detach()" invocation: expression.Detach()
var detachMethod = SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
expressionToFix, // The 'a' in 'b = a'
SyntaxFactory.IdentifierName("Detach")
);
var detachInvocation = SyntaxFactory.InvocationExpression(detachMethod);
// 4. Create the "new UniquePtr<T>(...)" expression
var newObjectCreation = SyntaxFactory.ObjectCreationExpression(typeSyntax)
.WithArgumentList(
SyntaxFactory.ArgumentList(
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Argument(detachInvocation)
)
)
)
.WithAdditionalAnnotations(Formatter.Annotation); // Auto-format whitespace
// 5. Replace the old node with the new node in the Syntax Tree
var root = await document.GetSyntaxRootAsync(cancellationToken);
var newRoot = root.ReplaceNode(expressionToFix, newObjectCreation);
return document.WithSyntaxRoot(newRoot);
}
}
}