feat: 解析器增强 (C#/Java) + 不可转换检测优化

增强内容:

C# 解析器增强 (CSharpParser.cs):
 泛型支持:TypeParameter, TypeArgument 提取
 特性支持:Attribute 检测
 async/await 检测
 LINQ 使用统计
 访问修饰符提取 (public/private/protected/internal)
 类修饰符 (abstract/static/sealed)
 方法修饰符 (virtual/override/async)
 属性 getter/setter 检测
 字段修饰符 (static/const/readonly)

Java 解析器增强 (JavaParser.cs):
 注解检测 (@Override, @Deprecated 等)
 泛型支持 (TypeParameters)
 record 类支持 (Java 16+)
 sealed/non-sealed 接口
 Lambda 表达式计数
 Stream API 使用统计
 Optional 使用统计
 注解提取
 方法修饰符 (static/final/synchronized)
 字段修饰符 (final/transient/volatile)
 throws 子句提取

不可转换检测优化 (TodoGenerator.cs):
 13 种 C# 特有模式检测 (LINQ/async/event/dynamic 等)
 7 种 Java 特有模式检测 (Stream/Lambda/Optional 等)
 替代方案建议
 置信度评分
 去重和排序

测试结果: 40 个测试通过 (2 个 Java 解析器测试需更新)

新增 PatternInfo 和 ConversionSuggestion 类用于模式匹配
Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com>
This commit is contained in:
monkeycode-ai
2026-06-04 02:36:22 +00:00
parent 71ef79a9e2
commit f772973b68
3 changed files with 436 additions and 132 deletions
+188 -44
View File
@@ -8,18 +8,13 @@ using CodePlay.Core.Interfaces;
namespace CodePlay.Core.Parsers;
/// <summary>
/// C# 语法解析器
/// C# 语法解析器 (增强版)
/// 支持泛型、特性、LINQ、async/await、模式匹配等高级特性
/// </summary>
public class CSharpParser : BaseParser
{
/// <summary>
/// 支持的语言类型
/// </summary>
public override LanguageType SupportedLanguage => LanguageType.CSharp;
/// <summary>
/// 解析 C# 源代码
/// </summary>
public override Task<Interfaces.SyntaxTree> ParseAsync(string sourceCode, CancellationToken cancellationToken = default)
{
var tree = CreateSyntaxTree();
@@ -28,25 +23,57 @@ public class CSharpParser : BaseParser
var compilationUnit = CSharpSyntaxTree.ParseText(sourceCode, cancellationToken: cancellationToken);
var root = compilationUnit.GetRoot(cancellationToken);
tree.Root = ConvertNode(root);
tree.Root = VisitNode(root);
ExtractComments(tree, root);
ExtractDocumentation(tree, root);
ExtractMetadata(tree, root);
return Task.FromResult(tree);
}
private Interfaces.SyntaxNode ConvertNode(Microsoft.CodeAnalysis.SyntaxNode node)
private Interfaces.SyntaxNode VisitNode(Microsoft.CodeAnalysis.SyntaxNode node)
{
var newNode = new Interfaces.SyntaxNode
{
Type = MapNodeType(node.Kind()),
Text = node.ToString(),
Metadata = new Dictionary<string, object?>()
Metadata = ExtractMetadataFromNode(node)
};
// 特殊处理泛型
if (node is TypeParameterListSyntax typeParams)
{
newNode.Metadata["TypeParameters"] = typeParams.Parameters.Select(p => p.Identifier.Text).ToList();
}
// 特殊处理特性
if (node is AttributeListSyntax attrList)
{
newNode.Metadata["Attributes"] = attrList.Attributes.Select(a => a.Name.ToString()).ToList();
}
// 特殊处理 async/await
if (node is MethodDeclarationSyntax methodDecl)
{
newNode.Metadata["IsAsync"] = methodDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.AsyncKeyword));
newNode.Metadata["ReturnType"] = methodDecl.ReturnType.ToString();
// 检测 LINQ
if (methodDecl.Body != null)
{
var hasLinq = methodDecl.Body.DescendantNodes()
.Any(n => n is InvocationExpressionSyntax inv &&
(inv.Expression.ToString().Contains("Where(") ||
inv.Expression.ToString().Contains("Select(") ||
inv.Expression.ToString().Contains("OrderBy(") ||
inv.Expression.ToString().Contains("FirstOrDefault(")));
newNode.Metadata["HasLinq"] = hasLinq;
}
}
foreach (var child in node.ChildNodes())
{
var childNode = ConvertNode(child);
var childNode = VisitNode(child);
childNode.Parent = newNode;
newNode.Children.Add(childNode);
}
@@ -54,38 +81,110 @@ public class CSharpParser : BaseParser
return newNode;
}
private Dictionary<string, object?> ExtractMetadataFromNode(Microsoft.CodeAnalysis.SyntaxNode node)
{
var metadata = new Dictionary<string, object?>();
switch (node)
{
case ClassDeclarationSyntax classDecl:
metadata["Name"] = classDecl.Identifier.Text;
metadata["IsAbstract"] = classDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword));
metadata["IsStatic"] = classDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword));
metadata["IsSealed"] = classDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.SealedKeyword));
metadata["BaseType"] = classDecl.BaseList?.Types.FirstOrDefault()?.ToString();
metadata["Interfaces"] = classDecl.BaseList?.Types
.Where(t => !(t is SimpleBaseTypeSyntax))
.Select(t => t.ToString()).ToList();
break;
case MethodDeclarationSyntax methodDecl:
metadata["Name"] = methodDecl.Identifier.Text;
metadata["ReturnType"] = methodDecl.ReturnType.ToString();
metadata["IsStatic"] = methodDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword));
metadata["IsVirtual"] = methodDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.VirtualKeyword));
metadata["IsOverride"] = methodDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.OverrideKeyword));
metadata["IsAbstract"] = methodDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword));
metadata["IsAsync"] = methodDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.AsyncKeyword));
metadata["AccessModifier"] = GetAccessModifier(methodDecl.Modifiers);
break;
case PropertyDeclarationSyntax propDecl:
metadata["Name"] = propDecl.Identifier.Text;
metadata["Type"] = propDecl.Type.ToString();
metadata["HasGetter"] = propDecl.AccessorList?.Accessors.Any(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)) == true;
metadata["HasSetter"] = propDecl.AccessorList?.Accessors.Any(a => a.Keyword.IsKind(SyntaxKind.SetKeyword)) == true;
metadata["IsVirtual"] = propDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.VirtualKeyword));
break;
case FieldDeclarationSyntax fieldDecl:
var variable = fieldDecl.Declaration.Variables.FirstOrDefault();
if (variable != null)
{
metadata["Name"] = variable.Identifier.Text;
metadata["Type"] = fieldDecl.Declaration.Type.ToString();
metadata["IsStatic"] = fieldDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword));
metadata["IsConst"] = fieldDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.ConstKeyword));
metadata["IsReadonly"] = fieldDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.ReadOnlyKeyword));
}
break;
}
return metadata;
}
private string GetAccessModifier(SyntaxTokenList modifiers)
{
if (modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword))) return "public";
if (modifiers.Any(m => m.IsKind(SyntaxKind.PrivateKeyword))) return "private";
if (modifiers.Any(m => m.IsKind(SyntaxKind.ProtectedKeyword))) return "protected";
if (modifiers.Any(m => m.IsKind(SyntaxKind.InternalKeyword))) return "internal";
return "default";
}
private SyntaxNodeType MapNodeType(Microsoft.CodeAnalysis.CSharp.SyntaxKind kind)
{
return kind switch
{
Microsoft.CodeAnalysis.CSharp.SyntaxKind.CompilationUnit => SyntaxNodeType.CompilationUnit,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.NamespaceDeclaration => SyntaxNodeType.Namespace,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ClassDeclaration => SyntaxNodeType.Class,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.InterfaceDeclaration => SyntaxNodeType.Interface,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.MethodDeclaration => SyntaxNodeType.Method,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.PropertyDeclaration => SyntaxNodeType.Property,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.FieldDeclaration => SyntaxNodeType.Field,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ConstructorDeclaration => SyntaxNodeType.Constructor,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.Parameter => SyntaxNodeType.Parameter,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.TypeArgumentList or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.GenericName => SyntaxNodeType.Type,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.Block or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExpressionStatement or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.IfStatement or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ForStatement or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ForEachStatement or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.WhileStatement or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ReturnStatement => SyntaxNodeType.Statement,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.InvocationExpression or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.AddExpression or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.SubtractExpression or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiplyExpression or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.DivideExpression or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.SimpleAssignmentExpression => SyntaxNodeType.Expression,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineCommentTrivia or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiLineCommentTrivia => SyntaxNodeType.Comment,
Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineDocumentationCommentTrivia or
Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiLineDocumentationCommentTrivia => SyntaxNodeType.DocumentationComment,
SyntaxKind.CompilationUnit => SyntaxNodeType.CompilationUnit,
SyntaxKind.NamespaceDeclaration or SyntaxKind.FileScopedNamespaceDeclaration => SyntaxNodeType.Namespace,
SyntaxKind.ClassDeclaration => SyntaxNodeType.Class,
SyntaxKind.InterfaceDeclaration => SyntaxNodeType.Interface,
SyntaxKind.StructDeclaration => SyntaxNodeType.Class,
SyntaxKind.RecordDeclaration => SyntaxNodeType.Class,
SyntaxKind.EnumDeclaration => SyntaxNodeType.Type,
SyntaxKind.MethodDeclaration => SyntaxNodeType.Method,
SyntaxKind.PropertyDeclaration => SyntaxNodeType.Property,
SyntaxKind.FieldDeclaration => SyntaxNodeType.Field,
SyntaxKind.ConstructorDeclaration => SyntaxNodeType.Constructor,
SyntaxKind.DestructorDeclaration => SyntaxNodeType.Method,
SyntaxKind.Parameter => SyntaxNodeType.Parameter,
SyntaxKind.TypeParameter => SyntaxNodeType.Type,
SyntaxKind.TypeArgumentList or SyntaxKind.GenericName => SyntaxNodeType.Type,
SyntaxKind.AttributeList => SyntaxNodeType.Type,
SyntaxKind.InvocationExpression or
SyntaxKind.SimpleAssignmentExpression or
SyntaxKind.AddExpression or
SyntaxKind.SubtractExpression or
SyntaxKind.MultiplyExpression or
SyntaxKind.DivideExpression => SyntaxNodeType.Expression,
SyntaxKind.Block or
SyntaxKind.ExpressionStatement or
SyntaxKind.IfStatement or
SyntaxKind.ForStatement or
SyntaxKind.ForEachStatement or
SyntaxKind.WhileStatement or
SyntaxKind.DoStatement or
SyntaxKind.ReturnStatement or
SyntaxKind.BreakStatement or
SyntaxKind.ContinueStatement or
SyntaxKind.SwitchStatement or
SyntaxKind.TryStatement or
SyntaxKind.ThrowStatement => SyntaxNodeType.Statement,
SyntaxKind.SingleLineCommentTrivia or
SyntaxKind.MultiLineCommentTrivia => SyntaxNodeType.Comment,
SyntaxKind.SingleLineDocumentationCommentTrivia or
SyntaxKind.MultiLineDocumentationCommentTrivia => SyntaxNodeType.DocumentationComment,
_ => SyntaxNodeType.Unknown
};
}
@@ -93,13 +192,13 @@ public class CSharpParser : BaseParser
private void ExtractComments(Interfaces.SyntaxTree tree, Microsoft.CodeAnalysis.SyntaxNode root)
{
var trivia = root.DescendantTrivia()
.Where(t => t.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineCommentTrivia) ||
t.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiLineCommentTrivia));
.Where(t => t.IsKind(SyntaxKind.SingleLineCommentTrivia) ||
t.IsKind(SyntaxKind.MultiLineCommentTrivia));
foreach (var commentTrivia in trivia)
{
var lineNumber = root.SyntaxTree.GetLineSpan(commentTrivia.Span).StartLinePosition.Line + 1;
var type = commentTrivia.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineCommentTrivia)
var type = commentTrivia.IsKind(SyntaxKind.SingleLineCommentTrivia)
? CommentType.SingleLine
: CommentType.MultiLine;
@@ -110,8 +209,8 @@ public class CSharpParser : BaseParser
private void ExtractDocumentation(Interfaces.SyntaxTree tree, Microsoft.CodeAnalysis.SyntaxNode root)
{
var docComments = root.DescendantTrivia()
.Where(t => t.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineDocumentationCommentTrivia) ||
t.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiLineDocumentationCommentTrivia));
.Where(t => t.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) ||
t.IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia));
foreach (var docComment in docComments)
{
@@ -126,4 +225,49 @@ public class CSharpParser : BaseParser
});
}
}
private void ExtractMetadata(Interfaces.SyntaxTree tree, Microsoft.CodeAnalysis.SyntaxNode root)
{
// 提取 Using 指令
var usings = root.DescendantNodes()
.OfType<UsingDirectiveSyntax>()
.Select(u => u.Name?.ToString() ?? "")
.Where(u => !string.IsNullOrEmpty(u));
tree.Root.Metadata["Usings"] = usings.ToList();
// 检测 async/await 使用
var asyncMethods = root.DescendantNodes()
.OfType<MethodDeclarationSyntax>()
.Where(m => m.Modifiers.Any(mod => mod.IsKind(SyntaxKind.AsyncKeyword)))
.Select(m => m.Identifier.Text)
.ToList();
tree.Root.Metadata["AsyncMethods"] = asyncMethods;
// 检测 LINQ 使用
var linqQueries = root.DescendantNodes()
.OfType<InvocationExpressionSyntax>()
.Where(inv =>
{
var name = inv.Expression.ToString();
return name.EndsWith("Where(") || name.EndsWith("Select(") ||
name.EndsWith("OrderBy(") || name.EndsWith("FirstOrDefault(") ||
name.EndsWith("Any(") || name.EndsWith("All(") ||
name.EndsWith("Count(") || name.EndsWith("ToList(");
})
.Count();
tree.Root.Metadata["LinqUsage"] = linqQueries;
// 检测泛型使用
var generics = root.DescendantNodes()
.OfType<TypeParameterListSyntax>()
.SelectMany(tp => tp.Parameters)
.Select(p => p.Identifier.Text)
.Distinct()
.ToList();
tree.Root.Metadata["Generics"] = generics;
}
}