mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-04 20:27:21 +00:00
Turned getter-like extension methods into extension properties (#1157)
This commit is contained in:
1 parent
0375318b1b
commit
f649febf88
286 files changed
+1379
-1109
No files matched your search
+47
-52
@@ -14,11 +14,11 @@ public class SwitchExpressionMethodAnalyzer : DiagnosticAnalyzer
|
||||
{
|
||||
private const string DIAGNOSTIC_ID = Identifier.SWITCH_EXPRESSION_METHOD_ANALYZER;
|
||||
|
||||
private static readonly string TITLE = "Method with switch expression should use inline expression body";
|
||||
private static readonly string TITLE = "Method or property with switch expression should use inline expression body";
|
||||
|
||||
private static readonly string MESSAGE_FORMAT = "Method with a switch expression should use inline expression body syntax with the switch keyword on the same line";
|
||||
private static readonly string MESSAGE_FORMAT = "{0} with a switch expression should use inline expression body syntax with the switch keyword on the same line";
|
||||
|
||||
private static readonly string DESCRIPTION = "Methods that only return a switch expression should use the expression body syntax (=>) with the switch keyword on the same line for better readability.";
|
||||
private static readonly string DESCRIPTION = "Methods and properties that only return a switch expression should use the expression body syntax (=>) with the switch keyword on the same line for better readability.";
|
||||
|
||||
private const string CATEGORY = "Style";
|
||||
|
||||
@@ -38,59 +38,54 @@ public class SwitchExpressionMethodAnalyzer : DiagnosticAnalyzer
|
||||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||
context.EnableConcurrentExecution();
|
||||
context.RegisterSyntaxNodeAction(AnalyzeMethodDeclaration, SyntaxKind.MethodDeclaration);
|
||||
context.RegisterSyntaxNodeAction(AnalyzePropertyDeclaration, SyntaxKind.PropertyDeclaration);
|
||||
}
|
||||
|
||||
private static void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
var methodDeclaration = (MethodDeclarationSyntax)context.Node;
|
||||
|
||||
// Fall 1: Methode hat Block-Body mit einem Return-Statement, das eine Switch-Expression ist
|
||||
if (methodDeclaration is { Body: not null, ExpressionBody: null })
|
||||
{
|
||||
var statements = methodDeclaration.Body.Statements;
|
||||
if (statements.Count == 1 && statements[0] is ReturnStatementSyntax { Expression: SwitchExpressionSyntax })
|
||||
{
|
||||
var diagnostic = Diagnostic.Create(RULE, methodDeclaration.Identifier.GetLocation());
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall 2: Methode hat Expression-Body, aber die Switch-Expression beginnt auf einer neuen Zeile
|
||||
var expressionBody = methodDeclaration.ExpressionBody;
|
||||
if (expressionBody?.Expression is SwitchExpressionSyntax switchExpr)
|
||||
{
|
||||
var arrowToken = expressionBody.ArrowToken;
|
||||
var switchToken = switchExpr.SwitchKeyword;
|
||||
bool hasNewLineBetweenArrowAndSwitch = false;
|
||||
|
||||
foreach (var trivia in arrowToken.TrailingTrivia)
|
||||
{
|
||||
if (trivia.IsKind(SyntaxKind.EndOfLineTrivia))
|
||||
{
|
||||
hasNewLineBetweenArrowAndSwitch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prüfe Leading Trivia des Switch-Keywords, falls notwendig
|
||||
if (!hasNewLineBetweenArrowAndSwitch)
|
||||
{
|
||||
foreach (var trivia in switchToken.LeadingTrivia)
|
||||
{
|
||||
if (trivia.IsKind(SyntaxKind.EndOfLineTrivia))
|
||||
{
|
||||
hasNewLineBetweenArrowAndSwitch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNewLineBetweenArrowAndSwitch)
|
||||
{
|
||||
var diagnostic = Diagnostic.Create(RULE, methodDeclaration.Identifier.GetLocation());
|
||||
context.ReportDiagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
if (NeedsInlineExpressionBody(methodDeclaration.Body, methodDeclaration.ExpressionBody))
|
||||
context.ReportDiagnostic(Diagnostic.Create(RULE, methodDeclaration.Identifier.GetLocation(), "Method"));
|
||||
}
|
||||
|
||||
private static void AnalyzePropertyDeclaration(SyntaxNodeAnalysisContext context)
|
||||
{
|
||||
var propertyDeclaration = (PropertyDeclarationSyntax)context.Node;
|
||||
if (propertyDeclaration.ExpressionBody is not null)
|
||||
{
|
||||
if (NeedsInlineExpressionBody(null, propertyDeclaration.ExpressionBody))
|
||||
context.ReportDiagnostic(Diagnostic.Create(RULE, propertyDeclaration.Identifier.GetLocation(), "Property"));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// A property with nothing but a getter which returns a switch expression says the same as one with
|
||||
// an expression body. Its getter is no place for the switch, wherever the keyword stands:
|
||||
//
|
||||
if (propertyDeclaration.AccessorList is not { Accessors.Count: 1 } accessorList)
|
||||
return;
|
||||
|
||||
var getter = accessorList.Accessors[0];
|
||||
if (!getter.IsKind(SyntaxKind.GetAccessorDeclaration) || getter.AttributeLists.Count > 0 || getter.Modifiers.Count > 0)
|
||||
return;
|
||||
|
||||
if (getter.ExpressionBody?.Expression is SwitchExpressionSyntax || ReturnsOnlySwitchExpression(getter.Body))
|
||||
context.ReportDiagnostic(Diagnostic.Create(RULE, propertyDeclaration.Identifier.GetLocation(), "Property"));
|
||||
}
|
||||
|
||||
private static bool NeedsInlineExpressionBody(BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody)
|
||||
{
|
||||
// Case 1: a block body with nothing but a return of a switch expression:
|
||||
if (body is not null && expressionBody is null)
|
||||
return ReturnsOnlySwitchExpression(body);
|
||||
|
||||
// Case 2: an expression body whose switch expression starts on a new line:
|
||||
if (expressionBody?.Expression is not SwitchExpressionSyntax switchExpression)
|
||||
return false;
|
||||
|
||||
return expressionBody.ArrowToken.TrailingTrivia.Any(SyntaxKind.EndOfLineTrivia) || switchExpression.SwitchKeyword.LeadingTrivia.Any(SyntaxKind.EndOfLineTrivia);
|
||||
}
|
||||
|
||||
private static bool ReturnsOnlySwitchExpression(BlockSyntax? body) => body is { Statements.Count: 1 } && body.Statements[0] is ReturnStatementSyntax { Expression: SwitchExpressionSyntax };
|
||||
}
|
||||
+66
-69
@@ -28,100 +28,97 @@ public class SwitchExpressionMethodCodeFixProvider : CodeFixProvider
|
||||
|
||||
var diagnostic = context.Diagnostics.First();
|
||||
var diagnosticSpan = diagnostic.Location.SourceSpan;
|
||||
var methodDeclaration = root.FindToken(diagnosticSpan.Start)
|
||||
var memberDeclaration = root.FindToken(diagnosticSpan.Start)
|
||||
.Parent?.AncestorsAndSelf()
|
||||
.OfType<MethodDeclarationSyntax>()
|
||||
.First();
|
||||
.OfType<MemberDeclarationSyntax>()
|
||||
.FirstOrDefault(member => member is MethodDeclarationSyntax or PropertyDeclarationSyntax);
|
||||
|
||||
if(methodDeclaration == null)
|
||||
if(memberDeclaration == null)
|
||||
return;
|
||||
|
||||
context.RegisterCodeFix(
|
||||
CodeAction.Create(
|
||||
title: "Use inline expression body for switch expression",
|
||||
createChangedDocument: c => UseInlineExpressionBodyAsync(context.Document, methodDeclaration, c),
|
||||
createChangedDocument: c => UseInlineExpressionBodyAsync(context.Document, memberDeclaration, c),
|
||||
equivalenceKey: nameof(SwitchExpressionMethodCodeFixProvider)),
|
||||
diagnostic);
|
||||
}
|
||||
|
||||
private static async Task<Document> UseInlineExpressionBodyAsync(Document document, MethodDeclarationSyntax methodDecl, CancellationToken cancellationToken)
|
||||
private static async Task<Document> UseInlineExpressionBodyAsync(Document document, MemberDeclarationSyntax memberDecl, CancellationToken cancellationToken)
|
||||
{
|
||||
var sourceText = await document.GetTextAsync(cancellationToken);
|
||||
var parameterText = methodDecl.ParameterList.ToString();
|
||||
var methodStartLine = sourceText.Lines.GetLineFromPosition(methodDecl.SpanStart);
|
||||
|
||||
SwitchExpressionSyntax? switchExpr = null;
|
||||
ExpressionSyntax? governingExpression = null;
|
||||
var switchBodyText = string.Empty;
|
||||
|
||||
if (methodDecl.Body != null)
|
||||
{
|
||||
// Case: Block-Body with a Return-Statement that contains a Switch-Expression
|
||||
var returnStmt = (ReturnStatementSyntax)methodDecl.Body.Statements[0];
|
||||
if (returnStmt.Expression is not SwitchExpressionSyntax matchingSwitchExpr)
|
||||
return document;
|
||||
|
||||
switchExpr = matchingSwitchExpr;
|
||||
governingExpression = switchExpr.GoverningExpression;
|
||||
|
||||
// Extract the switch body text:
|
||||
var switchStart = switchExpr.SwitchKeyword.SpanStart;
|
||||
var switchEnd = switchExpr.CloseBraceToken.Span.End;
|
||||
switchBodyText = sourceText.ToString(TextSpan.FromBounds(switchStart, switchEnd));
|
||||
}
|
||||
else if (methodDecl.ExpressionBody != null)
|
||||
{
|
||||
// Case 2: Expression-Body with a poorly formatted Switch-Expression
|
||||
switchExpr = (SwitchExpressionSyntax)methodDecl.ExpressionBody.Expression;
|
||||
governingExpression = switchExpr.GoverningExpression;
|
||||
|
||||
// Extract the switch body text:
|
||||
var switchStart = switchExpr.SwitchKeyword.SpanStart;
|
||||
var switchEnd = switchExpr.CloseBraceToken.Span.End;
|
||||
switchBodyText = sourceText.ToString(TextSpan.FromBounds(switchStart, switchEnd));
|
||||
}
|
||||
|
||||
if (switchExpr is null || governingExpression is null)
|
||||
var switchExpr = SwitchExpressionOf(memberDecl);
|
||||
var headerEnd = HeaderEndOf(memberDecl);
|
||||
if (switchExpr is null || headerEnd is null)
|
||||
return document;
|
||||
|
||||
var sourceText = await document.GetTextAsync(cancellationToken);
|
||||
var memberStartLine = sourceText.Lines.GetLineFromPosition(memberDecl.SpanStart);
|
||||
|
||||
//
|
||||
// Everything in front of the body stays as it was written: the attributes, the modifiers, the type,
|
||||
// the name, and of a method its type parameters, parameters, and constraints. Building it anew from
|
||||
// some of these parts loses the others.
|
||||
//
|
||||
var headerText = sourceText.ToString(TextSpan.FromBounds(memberDecl.SpanStart, headerEnd.Value)).TrimEnd();
|
||||
|
||||
// Extract the governing expression and the switch body:
|
||||
var govExprText = sourceText.ToString(governingExpression.Span);
|
||||
var govExprText = sourceText.ToString(switchExpr.GoverningExpression.Span);
|
||||
var switchBodyText = sourceText.ToString(TextSpan.FromBounds(switchExpr.OpenBraceToken.SpanStart, switchExpr.CloseBraceToken.Span.End));
|
||||
|
||||
// Create the new method with inline expression body and correct formatting:
|
||||
var returnTypeText = methodDecl.ReturnType.ToString();
|
||||
var modifiersText = string.Join(" ", methodDecl.Modifiers);
|
||||
var methodNameText = methodDecl.Identifier.Text;
|
||||
|
||||
// Determine the indentation of the method:
|
||||
var methodIndentation = "";
|
||||
for (var i = methodStartLine.Start; i < methodDecl.SpanStart; i++)
|
||||
// Determine the indentation of the member:
|
||||
var memberIndentation = "";
|
||||
for (var i = memberStartLine.Start; i < memberDecl.SpanStart; i++)
|
||||
{
|
||||
if (char.IsWhiteSpace(sourceText[i]))
|
||||
methodIndentation += sourceText[i];
|
||||
memberIndentation += sourceText[i];
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Erstelle die neue Methode mit Expression-Body und korrekter Formatierung
|
||||
var newMethodText = new StringBuilder();
|
||||
newMethodText.Append($"{modifiersText} {returnTypeText} {methodNameText}{parameterText} => {govExprText} switch");
|
||||
// Create the new member with an expression body and the switch keyword on the same line:
|
||||
var newMemberText = new StringBuilder();
|
||||
newMemberText.Append($"{headerText} => {govExprText} switch");
|
||||
|
||||
// Formatiere die geschweiften Klammern und den Switch-Body
|
||||
var switchBody = switchBodyText.Substring("switch".Length).Trim();
|
||||
// The switch cases are indented by four spaces more than the member:
|
||||
var caseIndentation = memberIndentation + " ";
|
||||
newMemberText.Append(FormatSwitchBody(switchBodyText, memberIndentation, caseIndentation));
|
||||
|
||||
// Bestimme die Einrückung für die Switch-Cases (4 Spaces oder 1 Tab mehr als die Methode)
|
||||
var caseIndentation = methodIndentation + " "; // 4 Spaces Einrückung
|
||||
|
||||
// Verarbeite die Klammern und formatiere den Body
|
||||
var formattedSwitchBody = FormatSwitchBody(switchBody, methodIndentation, caseIndentation);
|
||||
newMethodText.Append(formattedSwitchBody);
|
||||
|
||||
// Ersetze die alte Methoden-Deklaration mit dem neuen Text
|
||||
var newText = sourceText.Replace(methodDecl.Span, newMethodText.ToString());
|
||||
// Replace the old declaration with the new text:
|
||||
var newText = sourceText.Replace(memberDecl.Span, newMemberText.ToString());
|
||||
return document.WithText(newText);
|
||||
}
|
||||
|
||||
private static string FormatSwitchBody(string switchBody, string methodIndentation, string caseIndentation)
|
||||
private static SwitchExpressionSyntax? SwitchExpressionOf(MemberDeclarationSyntax memberDecl) => memberDecl switch
|
||||
{
|
||||
MethodDeclarationSyntax methodDecl => SwitchExpressionOf(methodDecl.Body, methodDecl.ExpressionBody),
|
||||
PropertyDeclarationSyntax { ExpressionBody: not null } propertyDecl => SwitchExpressionOf(null, propertyDecl.ExpressionBody),
|
||||
PropertyDeclarationSyntax { AccessorList.Accessors.Count: 1 } propertyDecl => SwitchExpressionOf(propertyDecl.AccessorList.Accessors[0].Body, propertyDecl.AccessorList.Accessors[0].ExpressionBody),
|
||||
|
||||
_ => null,
|
||||
};
|
||||
|
||||
private static SwitchExpressionSyntax? SwitchExpressionOf(BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody)
|
||||
{
|
||||
if (expressionBody?.Expression is SwitchExpressionSyntax inlineSwitchExpr)
|
||||
return inlineSwitchExpr;
|
||||
|
||||
// A block body with a return statement that contains a switch expression:
|
||||
if (body is { Statements.Count: 1 } && body.Statements[0] is ReturnStatementSyntax { Expression: SwitchExpressionSyntax returnedSwitchExpr })
|
||||
return returnedSwitchExpr;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int? HeaderEndOf(MemberDeclarationSyntax memberDecl) => memberDecl switch
|
||||
{
|
||||
MethodDeclarationSyntax { ConstraintClauses.Count: > 0 } methodDecl => methodDecl.ConstraintClauses.Last().Span.End,
|
||||
MethodDeclarationSyntax methodDecl => methodDecl.ParameterList.Span.End,
|
||||
PropertyDeclarationSyntax propertyDecl => propertyDecl.Identifier.Span.End,
|
||||
|
||||
_ => null,
|
||||
};
|
||||
|
||||
private static string FormatSwitchBody(string switchBody, string memberIndentation, string caseIndentation)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
|
||||
@@ -136,7 +133,7 @@ public class SwitchExpressionMethodCodeFixProvider : CodeFixProvider
|
||||
|
||||
// Add braces with correct indentation:
|
||||
result.AppendLine();
|
||||
result.Append($"{methodIndentation}{{");
|
||||
result.Append($"{memberIndentation}{{");
|
||||
|
||||
// Process each line of the switch body:
|
||||
var lines = bodyWithoutBraces.Split(["\r\n", "\n"], System.StringSplitOptions.None);
|
||||
@@ -155,7 +152,7 @@ public class SwitchExpressionMethodCodeFixProvider : CodeFixProvider
|
||||
|
||||
// Add the closing brace with correct indentation:
|
||||
result.AppendLine();
|
||||
result.Append($"{methodIndentation}}};");
|
||||
result.Append($"{memberIndentation}}};");
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user