public static IEnumerable<ReplaceAction> GetSimplifications(ForEachStatementSyntax forLoop, ISemanticModel model, Assumptions assumptions, CancellationToken cancellationToken = default(CancellationToken)) { if (forLoop.IsAnyIterationSufficient(model, assumptions) == true) yield break; // a more appropriate code issue handles this case // can the loop be replaced by its first or last iteration? var isFirstSufficient = forLoop.IsFirstIterationSufficient(model, assumptions) == true; var isLastSufficient = forLoop.IsLastIterationSufficient(model, assumptions) == true; var firstVsLast = isFirstSufficient ? "First" : isLastSufficient ? "Last" : null; if (firstVsLast == null) yield break; // do we know how to translate? var loopStatements = forLoop.Statement.Statements(); var rawThenStatements = loopStatements.SkipLast(loopStatements.Last().IsIntraLoopJump() ? 1 : 0).ToArray(); if (rawThenStatements.Any(e => e.HasTopLevelIntraLoopJumps())) yield break; // don't know how to translate jumps that aren't at the end // wrap collection items in a type with a new null value (so that a null result definitively indicates an empty collection) var iteratorType = ((LocalSymbol)model.GetDeclaredSymbol(forLoop)).Type; var nuller = GetNullabledQueryAndValueGetter(iteratorType, forLoop.Identifier, forLoop.Expression); var nullableQuery = nuller.Item1; var valueGetter = nuller.Item2; var tempNullableLocalName = Syntax.Identifier("_" + forLoop.Identifier.ValueText); var tempNullableLocalGet = Syntax.IdentifierName(tempNullableLocalName); // build replacement var iteratorReads = forLoop.Statement.ReadsOfLocalVariable(forLoop.Identifier).ToArray(); var desiredIterationQuery = nullableQuery.Accessing(firstVsLast + "OrDefault").Invoking(); var condition = tempNullableLocalGet.BOpNotEquals(Syntax.LiteralExpression(SyntaxKind.NullLiteralExpression)); var useDenulledLocal = iteratorReads.Length > 2; var thenStatement = useDenulledLocal ? rawThenStatements.Prepend(forLoop.Identifier.VarInit(valueGetter(tempNullableLocalGet))).Block() : rawThenStatements.Select(e => e.ReplaceNodes(iteratorReads, (n, a) => valueGetter(tempNullableLocalGet))).Block(); var replacementStatements = new StatementSyntax[] { tempNullableLocalName.VarInit(desiredIterationQuery), condition.IfThen(thenStatement) }; // expose as code action/issue yield return forLoop.MakeReplaceStatementWithManyAction( replacementStatements, "Execute " + firstVsLast + " if any"); }
public static IEnumerable<ReplaceAction> GetSimplifications(ForEachStatementSyntax forLoop, ISemanticModel model, Assumptions assumptions, CancellationToken cancellationToken = default(CancellationToken)) { // loop body idempotent and independent of the iterator? if (forLoop.IsAnyIterationSufficient(model, assumptions) != true) yield break; // build replacement if statement, if possible var loopStatements = forLoop.Statement.Statements(); if (loopStatements.None()) yield break; var ifBody = loopStatements.SkipLast(loopStatements.Last().IsIntraLoopJump() ? 1 : 0).Block(); if (ifBody.HasTopLevelIntraLoopJumps()) yield break; var ifCondition = forLoop.Expression.Accessing("Any").Invoking(); var rawReplacement = Syntax.IfStatement( condition: ifCondition, statement: ifBody); var replacement = rawReplacement.IncludingTriviaSurrounding(forLoop, TrivialTransforms.Placement.Around); // expose as code action/issue yield return new ReplaceAction( "Execute once if any", forLoop, replacement); }