1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
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;
}
}
}
|