00dc19585b
Co-authored-by: Raul Santos <raulsntos@gmail.com> Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com>
112 lines
4.7 KiB
C#
112 lines
4.7 KiB
C#
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CodeActions;
|
|
using Microsoft.CodeAnalysis.CodeFixes;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using Microsoft.CodeAnalysis.Diagnostics;
|
|
|
|
namespace Godot.SourceGenerators
|
|
{
|
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
|
public sealed class ClassPartialModifierAnalyzer : DiagnosticAnalyzer
|
|
{
|
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
|
|
ImmutableArray.Create(Common.ClassPartialModifierRule, Common.OuterClassPartialModifierRule);
|
|
|
|
public override void Initialize(AnalysisContext context)
|
|
{
|
|
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
|
context.EnableConcurrentExecution();
|
|
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration);
|
|
}
|
|
|
|
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
|
|
{
|
|
if (context.Node is not ClassDeclarationSyntax classDeclaration)
|
|
return;
|
|
|
|
if (context.ContainingSymbol is not INamedTypeSymbol typeSymbol)
|
|
return;
|
|
|
|
if (!typeSymbol.InheritsFrom("GodotSharp", GodotClasses.GodotObject))
|
|
return;
|
|
|
|
if (!classDeclaration.IsPartial())
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
Common.ClassPartialModifierRule,
|
|
classDeclaration.Identifier.GetLocation(),
|
|
typeSymbol.ToDisplayString()));
|
|
|
|
var outerClassDeclaration = context.Node.Parent as ClassDeclarationSyntax;
|
|
while (outerClassDeclaration is not null)
|
|
{
|
|
var outerClassTypeSymbol = context.SemanticModel.GetDeclaredSymbol(outerClassDeclaration);
|
|
if (outerClassTypeSymbol == null)
|
|
return;
|
|
|
|
if (!outerClassDeclaration.IsPartial())
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
Common.OuterClassPartialModifierRule,
|
|
outerClassDeclaration.Identifier.GetLocation(),
|
|
outerClassTypeSymbol.ToDisplayString()));
|
|
|
|
outerClassDeclaration = outerClassDeclaration.Parent as ClassDeclarationSyntax;
|
|
}
|
|
}
|
|
}
|
|
|
|
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
|
public sealed class ClassPartialModifierCodeFixProvider : CodeFixProvider
|
|
{
|
|
public override ImmutableArray<string> FixableDiagnosticIds =>
|
|
ImmutableArray.Create(Common.ClassPartialModifierRule.Id);
|
|
|
|
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
|
|
|
|
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
|
|
{
|
|
// Get the syntax root of the document.
|
|
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
|
|
|
|
// Get the diagnostic to fix.
|
|
var diagnostic = context.Diagnostics.First();
|
|
|
|
// Get the location of code issue.
|
|
var diagnosticSpan = diagnostic.Location.SourceSpan;
|
|
|
|
// Use that location to find the containing class declaration.
|
|
var classDeclaration = root?.FindToken(diagnosticSpan.Start)
|
|
.Parent?
|
|
.AncestorsAndSelf()
|
|
.OfType<ClassDeclarationSyntax>()
|
|
.First();
|
|
|
|
if (classDeclaration == null)
|
|
return;
|
|
|
|
context.RegisterCodeFix(
|
|
CodeAction.Create(
|
|
"Add partial modifier",
|
|
cancellationToken => AddPartialModifierAsync(context.Document, classDeclaration, cancellationToken),
|
|
classDeclaration.ToFullString()),
|
|
context.Diagnostics);
|
|
}
|
|
|
|
private static async Task<Document> AddPartialModifierAsync(Document document,
|
|
ClassDeclarationSyntax classDeclaration, CancellationToken cancellationToken)
|
|
{
|
|
// Create a new partial modifier.
|
|
var partialModifier = SyntaxFactory.Token(SyntaxKind.PartialKeyword);
|
|
var modifiedClassDeclaration = classDeclaration.AddModifiers(partialModifier);
|
|
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
|
|
// Replace the old class declaration with the modified one in the syntax root.
|
|
var newRoot = root!.ReplaceNode(classDeclaration, modifiedClassDeclaration);
|
|
var newDocument = document.WithSyntaxRoot(newRoot);
|
|
return newDocument;
|
|
}
|
|
}
|
|
}
|