using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using CodePlay.Core.Models;
using CodePlay.Core.Common;
using CodePlay.Core.Interfaces;
namespace CodePlay.Core.Parsers;
///
/// C# 语法解析器 (增强版)
/// 支持泛型、特性、LINQ、async/await、模式匹配等高级特性
///
public class CSharpParser : BaseParser
{
public override LanguageType SupportedLanguage => LanguageType.CSharp;
public override Task ParseAsync(string sourceCode, CancellationToken cancellationToken = default)
{
var tree = CreateSyntaxTree();
tree.SourceCode = sourceCode;
var compilationUnit = CSharpSyntaxTree.ParseText(sourceCode, cancellationToken: cancellationToken);
var root = compilationUnit.GetRoot(cancellationToken);
tree.Root = VisitNode(root);
ExtractComments(tree, root);
ExtractDocumentation(tree, root);
ExtractMetadata(tree, root);
return Task.FromResult(tree);
}
private Interfaces.SyntaxNode VisitNode(Microsoft.CodeAnalysis.SyntaxNode node)
{
var newNode = new Interfaces.SyntaxNode
{
Type = MapNodeType(node.Kind()),
Text = node.ToString(),
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 = VisitNode(child);
childNode.Parent = newNode;
newNode.Children.Add(childNode);
}
return newNode;
}
private Dictionary ExtractMetadataFromNode(Microsoft.CodeAnalysis.SyntaxNode node)
{
var metadata = new Dictionary();
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
{
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
};
}
private void ExtractComments(Interfaces.SyntaxTree tree, Microsoft.CodeAnalysis.SyntaxNode root)
{
var trivia = root.DescendantTrivia()
.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(SyntaxKind.SingleLineCommentTrivia)
? CommentType.SingleLine
: CommentType.MultiLine;
AddComment(tree, commentTrivia.ToString(), type, lineNumber);
}
}
private void ExtractDocumentation(Interfaces.SyntaxTree tree, Microsoft.CodeAnalysis.SyntaxNode root)
{
var docComments = root.DescendantTrivia()
.Where(t => t.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) ||
t.IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia));
foreach (var docComment in docComments)
{
var node = root.FindNode(docComment.Span);
var lineNumber = root.SyntaxTree.GetLineSpan(docComment.Span).StartLinePosition.Line + 1;
tree.Documentation.Add(new SyntaxDocumentation
{
ElementName = node.Parent?.ToString() ?? "Unknown",
Content = docComment.ToString(),
Format = DocFormat.XmlDoc
});
}
}
private void ExtractMetadata(Interfaces.SyntaxTree tree, Microsoft.CodeAnalysis.SyntaxNode root)
{
// 提取 Using 指令
var usings = root.DescendantNodes()
.OfType()
.Select(u => u.Name?.ToString() ?? "")
.Where(u => !string.IsNullOrEmpty(u));
tree.Root.Metadata["Usings"] = usings.ToList();
// 检测 async/await 使用
var asyncMethods = root.DescendantNodes()
.OfType()
.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()
.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()
.SelectMany(tp => tp.Parameters)
.Select(p => p.Identifier.Text)
.Distinct()
.ToList();
tree.Root.Metadata["Generics"] = generics;
}
}