Skip to content

Commit

Permalink
chore: simplify further
Browse files Browse the repository at this point in the history
  • Loading branch information
dupdob committed Jan 21, 2024
1 parent 2138f15 commit 363925c
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 109 deletions.
98 changes: 47 additions & 51 deletions src/Stryker.Core/Stryker.Core/Helpers/RoslynHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@ namespace Stryker.Core.Helpers
{
internal static class RoslynHelper
{
/// <summary>
/// Check if an expression is a string.
/// </summary>
/// <param name="node">Expression to check</param>
/// <returns>true if it is a string</returns>
public static bool IsAStringExpression(this ExpressionSyntax node) =>
node.Kind() == SyntaxKind.StringLiteralExpression ||
node.Kind() == SyntaxKind.InterpolatedStringExpression;

/// <summary>
/// Check if an expression contains a declaration
/// </summary>
/// <param name="node">expression to check</param>
/// <returns>true if it contains a declaration</returns>
public static bool ContainsDeclarations(this ExpressionSyntax node) =>
node.ContainsNodeThatVerifies(x =>
x.IsKind(SyntaxKind.DeclarationExpression) || x.IsKind(SyntaxKind.DeclarationPattern));

/// <summary>
/// Gets the return type of a method (incl. constructor, destructor...)
/// </summary>
Expand Down Expand Up @@ -66,41 +84,6 @@ public static TypeSyntax ReturnType(this AccessorDeclarationSyntax accessor)
/// <returns>true when the block contains no statement.</returns>
public static bool IsEmpty(this BlockSyntax blockSyntax) => blockSyntax.Statements.Count == 0;

/// <summary>
/// Checks if a method ends with return.
/// </summary>
/// <param name="baseMethod">method to analyze.</param>
/// <returns>true if <paramref name="baseMethod"/>is non-void method, an operator or a conversion operator.</returns>
public static bool NeedsReturn(this BaseMethodDeclarationSyntax baseMethod) =>
baseMethod switch
{
MethodDeclarationSyntax method => !(method.ReturnType is PredefinedTypeSyntax predefinedType &&
predefinedType.Keyword.IsKind(SyntaxKind.VoidKeyword)),
OperatorDeclarationSyntax => true,
ConversionOperatorDeclarationSyntax => true,
_ => false
};

/// <summary>
/// Checks if a syntax node returns a value.
/// </summary>
/// <param name="node">syntax node to check</param>
/// <returns>true is this a method, a function having non-void return value, or a getter.</returns>
public static bool NeedsReturn(this SyntaxNode node) => node switch
{
BaseMethodDeclarationSyntax baseMethod => baseMethod switch
{
MethodDeclarationSyntax method => !(method.ReturnType is PredefinedTypeSyntax predefinedType &&
predefinedType.Keyword.IsKind(SyntaxKind.VoidKeyword)),
OperatorDeclarationSyntax => true,
ConversionOperatorDeclarationSyntax => true,
_ => false
},
AccessorDeclarationSyntax accessor => accessor.IsGetter(),
LocalFunctionStatementSyntax localFunction => !localFunction.ReturnType.IsVoid(),
_ => false
};

/// <summary>
/// Checks if a member is static
/// </summary>
Expand Down Expand Up @@ -157,21 +140,14 @@ public static AccessorDeclarationSyntax GetAccessor(this PropertyDeclarationSynt
/// <returns>An expression representing `default(<paramref name="type"/>'.</returns>
public static ExpressionSyntax BuildDefaultExpression(this TypeSyntax type) => SyntaxFactory.DefaultExpression(type.WithoutTrivia()).WithLeadingTrivia(SyntaxFactory.Space);

/// <summary>
/// Check (recursively) if the statement returns some value.
/// </summary>
/// <param name="syntax">statement to analyze.</param>
/// <returns>true is the statement returns a value in at least one path.</returns>
public static bool ContainsValueReturn(this StatementSyntax syntax) => syntax.ScanChildStatements(s => s is ReturnStatementSyntax { Expression: not null });

/// <summary>
/// Check if a statements (or one of its child statements, if any) verifies some given predicate.
/// </summary>
/// <param name="syntax">initial statements</param>
/// <param name="predicate">predicate to verify</param>
/// <returns>true if any of the child statements verify the predicate</returns>
/// <remarks>scanning stops as soon as one child matches the predicate</remarks>
public static bool ScanChildStatements(this StatementSyntax syntax, Func<StatementSyntax, bool> predicate)
public static bool ScanChildStatements(this StatementSyntax syntax, Func<StatementSyntax, bool> predicate, bool skipBlocks = false)
{
if (predicate(syntax))
{
Expand All @@ -181,27 +157,47 @@ public static bool ScanChildStatements(this StatementSyntax syntax, Func<Stateme
switch (syntax)
{
case BlockSyntax block:
return block.Statements.Any(s => ScanChildStatements(s, predicate));
return !skipBlocks && block.Statements.Any(s => s.ScanChildStatements(predicate, false));
case LocalFunctionStatementSyntax:
return false;
case ForStatementSyntax forStatement:
return forStatement.Statement.ScanChildStatements(predicate);
return forStatement.Statement.ScanChildStatements(predicate, skipBlocks);
case WhileStatementSyntax whileStatement:
return whileStatement.Statement.ScanChildStatements(predicate);
return whileStatement.Statement.ScanChildStatements(predicate, skipBlocks);
case DoStatementSyntax doStatement:
return doStatement.Statement.ScanChildStatements(predicate);
return doStatement.Statement.ScanChildStatements(predicate, skipBlocks);
case SwitchStatementSyntax switchStatement:
return switchStatement.Sections.SelectMany(s => s.Statements).Any(statement => ScanChildStatements(statement, predicate));
return switchStatement.Sections.SelectMany(s => s.Statements).Any(statement => statement.ScanChildStatements(predicate, skipBlocks));
case IfStatementSyntax ifStatement:
if (ifStatement.Statement.ScanChildStatements(predicate))
if (ifStatement.Statement.ScanChildStatements(predicate, skipBlocks))
return true;
return ifStatement.Else?.Statement.ScanChildStatements(predicate) == true;
return ifStatement.Else?.Statement.ScanChildStatements(predicate, skipBlocks) == true;
default:
return syntax.ChildNodes().Where(n => n is StatementSyntax).Cast<StatementSyntax>()
.Any(s => ScanChildStatements(s, predicate));
.Any(s => s.ScanChildStatements(predicate, skipBlocks));
}
}

/// <summary>
/// Scans recursively the node to find if a child node matches the predicated. Does not recurse into local functions or anonymous functions
/// </summary>
/// <param name="node">starting node</param>
/// <param name="predicate">predicate to match</param>
/// <param name="skipBlock">set to true to avoid scan into block statements</param>
/// <returns></returns>
public static bool ContainsNodeThatVerifies(this SyntaxNode node, Func<SyntaxNode, bool> predicate, bool skipBlock = true) =>
// scan
node.DescendantNodes((child) =>
{
if (skipBlock && child is BlockSyntax)
{
return false;
}

return (child.Parent is not AnonymousFunctionExpressionSyntax function || function.ExpressionBody != child)
&& (child.Parent is not LocalFunctionStatementSyntax localFunction || localFunction.ExpressionBody != child);
} ).Any(predicate);

public static bool HasAMemberBindingExpression(this ExpressionSyntax expression) =>
expression switch
{
Expand Down
53 changes: 0 additions & 53 deletions src/Stryker.Core/Stryker.Core/Helpers/SyntaxHelper.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public BlockSyntax InjectReturn(BlockSyntax block, TypeSyntax type)
|| block.Statements.Count == 0
|| block.Statements.Last().Kind() == SyntaxKind.ReturnStatement
|| type.IsVoid()
|| block.ContainsNodeThatVerifies(x => x.IsKind(SyntaxKind.YieldReturnStatement) || x.IsKind(SyntaxKind.YieldBreakStatement), false)
|| !block.ContainsNodeThatVerifies(x => x.IsKind(SyntaxKind.ReturnStatement), false))
|| block.ScanChildStatements(x => x.IsKind(SyntaxKind.YieldReturnStatement) || x.IsKind(SyntaxKind.YieldBreakStatement))
|| !block.ScanChildStatements(x => x.IsKind(SyntaxKind.ReturnStatement)))
{
return block;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Stryker.Core.Helpers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ protected override BasePropertyDeclarationSyntax OrchestrateChildrenMutation(Pro
MutateSingleNode(original, semanticModel, original == node.Initializer ? context.EnterStatic() : context));
if (children.Initializer != null)
{
children = children.ReplaceNode(children.Initializer.Value,
context.PlaceStaticContextMarker(children.Initializer.Value));
children = children.WithInitializer(children.Initializer.WithValue(context.PlaceStaticContextMarker(children.Initializer.Value)));
}
return children;
}
Expand Down
4 changes: 4 additions & 0 deletions src/Stryker.Core/Stryker.Core/Mutants/MutantPlacer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ static MutantPlacer()
/// <param name="requireRecursive"></param>
public static void RegisterEngine(IInstrumentCode engine, bool requireRecursive = false)
{
if (InstrumentEngines.TryGetValue(engine.InstrumentEngineId, out var existing) && existing!.GetType() != engine.GetType())
{
throw new InvalidOperationException($"Cannot register {engine.GetType().Name} as name {engine.InstrumentEngineId} is already registered to {existing.GetType().Name}.");
}
InstrumentEngines[engine.InstrumentEngineId] = engine;
if (requireRecursive)
{
Expand Down

0 comments on commit 363925c

Please sign in to comment.