using Codelyzer.Analysis.Common;
using Codelyzer.Analysis.CSharp.Handlers;
using Codelyzer.Analysis.Model;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Codelyzer.Analysis.CSharp
{
///
/// Processor that traverses the Syntax tree nodes
///
public class CSharpRoslynProcessor : CSharpSyntaxVisitor, IDisposable
{
private readonly CodeContext _context;
protected SemanticModel SemanticModel { get => _context.SemanticModel; }
protected SyntaxTree SyntaxTree { get => _context.SyntaxTree; }
protected ILogger Logger { get => _context.Logger; }
protected MetaDataSettings MetaDataSettings { get => _context.AnalyzerConfiguration.MetaDataSettings; }
protected RootUstNode RootNode { get; set; }
public CSharpRoslynProcessor(CodeContext context)
{
_context = context;
}
///
/// Start traversing the syntax tree
///
/// The node to start the traversal from
///
[return: MaybeNull]
public override UstNode Visit(SyntaxNode node)
{
if (node == null)
{
return null;
}
if (RootNode == null)
{
RootNode = new RootUstNode();
}
var children = new List();
foreach (SyntaxNode child in node.ChildNodes())
{
var result = HandleGenericVisit(child);
if (result != null)
{
children.Add(result);
}
}
RootNode.SetPaths(_context.SourceFilePath, SyntaxTree.FilePath);
RootNode.Language = node.Language;
RootNode.Children.AddRange(children);
AssignParentNode(RootNode.Children, RootNode);
return RootNode;
}
[return: MaybeNull]
public override UstNode DefaultVisit(SyntaxNode node)
{
return null;
}
/* ---- Overrides ----------------------*/
public override UstNode VisitUsingDirective(UsingDirectiveSyntax node)
{
UsingDirectiveHandler handler = new UsingDirectiveHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
{
NamespaceDeclarationHandler handler = new NamespaceDeclarationHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node)
{
NamespaceDeclarationHandler handler = new NamespaceDeclarationHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
ClassDeclarationHandler handler = new ClassDeclarationHandler(_context, node);
HandleReferences(((ClassDeclaration)handler.UstNode).Reference);
return handler.UstNode;
}
public override UstNode VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
{
if (!MetaDataSettings.InterfaceDeclarations) return null;
InterfaceDeclarationHandler handler = new InterfaceDeclarationHandler(_context, node);
HandleReferences(((InterfaceDeclaration)handler.UstNode).Reference);
return handler.UstNode;
}
public override UstNode VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
{
ConstructorDeclarationHandler handler = new ConstructorDeclarationHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitMethodDeclaration(MethodDeclarationSyntax node)
{
MethodDeclarationHandler handler = new MethodDeclarationHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitReturnStatement(ReturnStatementSyntax node)
{
ReturnStatementHandler handler = new ReturnStatementHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitBlock(BlockSyntax node)
{
BlockStatementHandler handler = new BlockStatementHandler(_context, node);
if (!string.IsNullOrEmpty(handler.UstNode.Identifier))
{
return handler.UstNode;
}
else
{
return null;
}
}
public override UstNode VisitArrowExpressionClause(ArrowExpressionClauseSyntax node)
{
ArrowExpressionClauseHandler handler = new ArrowExpressionClauseHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitExpressionStatement(ExpressionStatementSyntax node)
{
return base.VisitExpressionStatement(node);
}
public override UstNode VisitLiteralExpression(LiteralExpressionSyntax node)
{
if (!MetaDataSettings.LiteralExpressions) return null;
LiteralExpressionHandler handler = new LiteralExpressionHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitInvocationExpression(InvocationExpressionSyntax node)
{
if (!MetaDataSettings.MethodInvocations) return null;
InvocationExpressionHandler handler = new InvocationExpressionHandler(_context, node);
HandleReferences(((InvocationExpression)handler.UstNode).Reference);
return handler.UstNode;
}
public override UstNode VisitArgument(ArgumentSyntax node)
{
if (!MetaDataSettings.InvocationArguments) return null;
ArgumentHandler handler = new ArgumentHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitObjectCreationExpression(ObjectCreationExpressionSyntax node)
{
ObjectCreationExpressionHandler handler = new ObjectCreationExpressionHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitAttribute(AttributeSyntax node)
{
if (!MetaDataSettings.Annotations) return null;
AttributeHandler handler = new AttributeHandler(_context, node);
HandleReferences(((Annotation)handler.UstNode).Reference);
return handler.UstNode;
}
public override UstNode VisitAttributeArgument(AttributeArgumentSyntax node)
{
if (!MetaDataSettings.Annotations) return null;
AttributeArgumentHandler handler = new AttributeArgumentHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitIdentifierName(IdentifierNameSyntax node)
{
if (MetaDataSettings.DeclarationNodes)
{
IdentifierNameHandler handler = new IdentifierNameHandler(_context, node);
if (!string.IsNullOrEmpty(handler.UstNode.Identifier))
{
HandleReferences(((DeclarationNode)handler.UstNode).Reference);
return handler.UstNode;
}
}
return null;
}
public override UstNode VisitEnumDeclaration(EnumDeclarationSyntax node)
{
if (MetaDataSettings.EnumDeclarations)
{
EnumDeclarationHandler handler = new EnumDeclarationHandler(_context, node);
if (!string.IsNullOrEmpty(handler.UstNode.Identifier))
{
HandleReferences(((EnumDeclaration)handler.UstNode).Reference);
return handler.UstNode;
}
}
return null;
}
public override UstNode VisitStructDeclaration(StructDeclarationSyntax node)
{
if (!MetaDataSettings.StructDeclarations) return null;
StructDeclarationHandler handler = new StructDeclarationHandler(_context, node);
if (!string.IsNullOrEmpty(handler.UstNode.Identifier))
{
HandleReferences(((StructDeclaration)handler.UstNode).Reference);
}
return handler.UstNode;
}
public override UstNode VisitElementAccessExpression(ElementAccessExpressionSyntax node)
{
if (!MetaDataSettings.ElementAccess) return null;
var handler = new ElementAccessExpressionHandler(_context, node);
HandleReferences(((ElementAccess)handler.UstNode).Reference);
return handler.UstNode;
}
public override UstNode VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
{
if (!MetaDataSettings.MemberAccess) return null;
var handler = new MemberAccessExpressionHandler(_context, node);
HandleReferences(((MemberAccess)handler.UstNode).Reference);
return handler.UstNode;
}
public override UstNode VisitSimpleLambdaExpression(SimpleLambdaExpressionSyntax node)
{
if (!MetaDataSettings.LambdaMethods) return null;
var handler = new SimpleLambdaExpressionHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitParenthesizedLambdaExpression(ParenthesizedLambdaExpressionSyntax node)
{
if (!MetaDataSettings.LambdaMethods) return null;
var handler = new ParenthesizedLambdaExpressionHandler(_context, node);
return handler.UstNode;
}
public override UstNode VisitRecordDeclaration(RecordDeclarationSyntax node)
{
RecordDeclarationHandler handler = new RecordDeclarationHandler(_context, node);
HandleReferences(((RecordDeclaration)handler.UstNode).Reference);
return handler.UstNode;
}
private void HandleReferences(in Reference reference)
{
if (MetaDataSettings.ReferenceData
&& !RootNode.References.Contains(reference))
{
var rootReference = new Reference() { Assembly = reference.Assembly, Namespace = reference.Namespace, Version = reference.Version };
if (reference.AssemblySymbol != null)
{
var metaDataReference = SemanticModel?.Compilation.GetMetadataReference(reference.AssemblySymbol);
if (metaDataReference != null)
{
rootReference.AssemblyLocation = metaDataReference.Display;
}
}
RootNode.References.Add(rootReference);
}
}
private List HandleGenericMembers(List children)
{
List childUstNodes = new List();
foreach (var child in children)
{
var childUstNode = HandleGenericVisit(child);
if (childUstNode != null)
{
childUstNodes.Add(childUstNode);
}
//If we're not handling the node, we'll collapse the level into the parent so that we can still visit the children
else
{
var grandChildren = child.ChildNodes();
if (grandChildren.Any())
{
var grandChildUstNodes = HandleGenericMembers(grandChildren.ToList());
if (grandChildUstNodes.Any())
{
childUstNodes.AddRange(grandChildUstNodes);
}
}
}
}
return childUstNodes;
}
private UstNode HandleGenericVisit(SyntaxNode node)
{
try
{
var ustNode = base.Visit(node);
if (ustNode != null)
{
AddChildNodes(ustNode.Children, node);
AssignParentNode(ustNode.Children, ustNode);
}
return ustNode;
}
catch (Exception ex)
{
Logger.LogError(ex, node.ToString());
return null;
}
}
private void AddChildNodes(UstList nodeChildren, SyntaxNode syntaxNode)
{
var children = HandleGenericMembers(syntaxNode.ChildNodes()?.ToList());
if (children != null && nodeChildren != null)
{
nodeChildren.AddRange(children);
}
}
private void AssignParentNode(List children, UstNode parentNode)
{
foreach (var child in children)
{
child.Parent = parentNode;
}
}
public void Dispose()
{
_context?.Dispose();
RootNode = null;
}
}
}