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; } }