Ejemplo n.º 1
0
        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");
        }