private void MakeInterpolatedStringFormat(
            BoundInterpolatedString node,
            out BoundExpression format,
            out ArrayBuilder <BoundExpression> expressions
            )
        {
            _factory.Syntax = node.Syntax;
            int n             = node.Parts.Length - 1;
            var formatString  = PooledStringBuilder.GetInstance();
            var stringBuilder = formatString.Builder;

            expressions = ArrayBuilder <BoundExpression> .GetInstance(n + 1);

            int nextFormatPosition = 0;

            for (int i = 0; i <= n; i++)
            {
                var part   = node.Parts[i];
                var fillin = part as BoundStringInsert;
                if (fillin == null)
                {
                    Debug.Assert(part is BoundLiteral && part.ConstantValue != null);
                    // this is one of the literal parts
                    stringBuilder.Append(part.ConstantValue.StringValue);
                }
                else
                {
                    // this is one of the expression holes
                    stringBuilder.Append('{').Append(nextFormatPosition++);
                    if (fillin.Alignment != null && !fillin.Alignment.HasErrors)
                    {
                        Debug.Assert(fillin.Alignment.ConstantValue is { });
Пример #2
0
 protected override Conversion GetInterpolatedStringConversion(BoundInterpolatedString source, TypeSymbol destination, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
 {
     // An interpolated string expression may be converted to the types
     // System.IFormattable and System.FormattableString
     return((destination == Compilation.GetWellKnownType(WellKnownType.System_IFormattable) ||
             destination == Compilation.GetWellKnownType(WellKnownType.System_FormattableString))
         ? Conversion.InterpolatedString : Conversion.NoConversion);
 }
Пример #3
0
 protected override Conversion GetInterpolatedStringConversion(BoundInterpolatedString source, TypeSymbol destination, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
 {
     // An interpolated string expression may be converted to the types
     // System.IFormattable and System.FormattableString
     return((TypeSymbol.Equals(destination, Compilation.GetWellKnownType(WellKnownType.System_IFormattable), TypeCompareKind.ConsiderEverything2) ||
             TypeSymbol.Equals(destination, Compilation.GetWellKnownType(WellKnownType.System_FormattableString), TypeCompareKind.ConsiderEverything2))
         ? Conversion.InterpolatedString : Conversion.NoConversion);
 }
        public override BoundNode VisitInterpolatedString(BoundInterpolatedString node)
        {
            //
            // We lower an interpolated string into an invocation of String.Format.  For example, we translate the expression
            //
            //     $"Jenny don\'t change your number { 8675309 }"
            //
            // into
            //
            //     String.Format("Jenny don\'t change your number {0}", new object[] { 8675309 })
            //

            Debug.Assert(node.Type.SpecialType == SpecialType.System_String); // if target-converted, we should not get here.
            BoundExpression format;
            ArrayBuilder <BoundExpression> expressions;

            MakeInterpolatedStringFormat(node, out format, out expressions);
            if (expressions.Count == 0)
            {
                // There are no fill-ins. Handle the escaping of {{ and }} and return the value.
                Debug.Assert(!format.HasErrors && format.ConstantValue != null && format.ConstantValue.IsString);
                var builder      = PooledStringBuilder.GetInstance();
                var formatText   = format.ConstantValue.StringValue;
                int formatLength = formatText.Length;
                for (int i = 0; i < formatLength; i++)
                {
                    char c = formatText[i];
                    builder.Builder.Append(c);
                    if ((c == '{' || c == '}') && (i + 1) < formatLength && formatText[i + 1] == c)
                    {
                        i++;
                    }
                }
                return(factory.StringLiteral(builder.ToStringAndFree()));
            }

            // The normal pattern for lowering is to lower subtrees before the enclosing tree. However we cannot lower
            // the arguments first in this situation because we do not know what conversions will be
            // produced for the arguments until after we've done overload resolution. So we produce the invocation
            // and then lower it along with its arguments.
            expressions.Insert(0, format);
            var stringType = node.Type;
            var result     = factory.StaticCall(stringType, "Format", expressions.ToImmutableAndFree(),
                                                allowUnexpandedForm: false // if an interpolation expression is the null literal, it should not match a params parameter.
                                                );

            if (!result.HasAnyErrors)
            {
                result = VisitExpression(result); // lower the arguments AND handle expanded form, argument conversions, etc.
                result = MakeConversion(result, node.Type, @checked: false);
            }
            return(result);
        }
        private void MakeInterpolatedStringFormat(BoundInterpolatedString node, out BoundExpression format, out ArrayBuilder <BoundExpression> expressions)
        {
            _factory.Syntax = node.Syntax;
            int n             = node.Parts.Length - 1;
            var formatString  = PooledStringBuilder.GetInstance();
            var stringBuilder = formatString.Builder;

            expressions = ArrayBuilder <BoundExpression> .GetInstance(n + 1);

            int nextFormatPosition = 0;

            for (int i = 0; i <= n; i++)
            {
                var part   = node.Parts[i];
                var fillin = part as BoundStringInsert;
                if (fillin == null)
                {
                    Debug.Assert(part is BoundLiteral && part.ConstantValue != null);
                    // this is one of the literal parts
                    stringBuilder.Append(part.ConstantValue.StringValue);
                }
                else
                {
                    // this is one of the expression holes
                    stringBuilder.Append('{').Append(nextFormatPosition++);
                    if (fillin.Alignment != null && !fillin.Alignment.HasErrors)
                    {
                        stringBuilder.Append(',').Append(fillin.Alignment.ConstantValue.Int64Value);
                    }
                    if (fillin.Format != null && !fillin.Format.HasErrors)
                    {
                        stringBuilder.Append(':').Append(fillin.Format.ConstantValue.StringValue);
                    }
                    stringBuilder.Append('}');
                    var value = fillin.Value;
                    if (value.Type?.TypeKind == TypeKind.Dynamic)
                    {
                        value = MakeConversionNode(value, _compilation.ObjectType, @checked: false);
                    }

                    expressions.Add(value); // NOTE: must still be lowered
                }
            }

            format = _factory.StringLiteral(formatString.ToStringAndFree());
        }
        private static bool CanLowerToStringConcatenation(BoundInterpolatedString node)
        {
            foreach (var part in node.Parts)
            {
                if (part is BoundStringInsert fillin)
                {
                    // this is one of the expression holes
                    if (fillin.HasErrors ||
                        fillin.Value.Type?.SpecialType != SpecialType.System_String ||
                        fillin.Alignment != null ||
                        fillin.Format != null)
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }
        private void MakeInterpolatedStringFormat(BoundInterpolatedString node, out BoundExpression format, out ArrayBuilder <BoundExpression> expressions)
        {
            factory.Syntax = node.Syntax;
            int n            = node.Parts.Length - 1;
            var formatString = PooledStringBuilder.GetInstance();

            expressions = ArrayBuilder <BoundExpression> .GetInstance(n + 1);

            int nextFormatPosition = 0;

            for (int i = 0; i <= n; i++)
            {
                var part   = node.Parts[i];
                var fillin = part as BoundStringInsert;
                if (fillin == null)
                {
                    // this is one of the literal parts
                    formatString.Builder.Append(part.ConstantValue.StringValue);
                }
                else
                {
                    // this is one of the expression holes
                    formatString.Builder.Append("{").Append(nextFormatPosition++);
                    if (fillin.Alignment != null && !fillin.Alignment.HasErrors)
                    {
                        formatString.Builder.Append(",").Append(fillin.Alignment.ConstantValue.Int64Value);
                    }
                    if (fillin.Format != null && !fillin.Format.HasErrors)
                    {
                        formatString.Builder.Append(":").Append(fillin.Format.ConstantValue.StringValue);
                    }
                    formatString.Builder.Append("}");
                    expressions.Add(fillin.Value); // NOTE: must still be lowered
                }
            }

            format = factory.StringLiteral(formatString.ToStringAndFree());
        }
Пример #8
0
 protected override Conversion GetInterpolatedStringConversion(BoundInterpolatedString source, TypeSymbol destination, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
 {
     // Conversions involving interpolated strings require a Binder.
     throw ExceptionUtilities.Unreachable;
 }
        public override BoundNode VisitInterpolatedString(BoundInterpolatedString node)
        {
            Debug.Assert(node.Type.SpecialType == SpecialType.System_String); // if target-converted, we should not get here.

            BoundExpression result;

            if (CanLowerToStringConcatenation(node))
            {
                // All fill-ins, if any, are strings, and none of them have alignment or format specifiers.
                // We can lower to a more efficient string concatenation
                // The normal pattern for lowering is to lower subtrees before the enclosing tree. However in this case
                // we want to lower the entire concatenation so we get the optimizations done by that lowering (e.g. constant folding).

                int length = node.Parts.Length;
                if (length == 0)
                {
                    // $"" -> ""
                    return(_factory.StringLiteral(""));
                }

                result = null;
                for (int i = 0; i < length; i++)
                {
                    var part = node.Parts[i];
                    if (part is BoundStringInsert fillin)
                    {
                        // this is one of the filled-in expressions
                        part = fillin.Value;
                    }
                    else
                    {
                        // this is one of the literal parts
                        Debug.Assert(part is BoundLiteral && part.ConstantValue != null);
                        part = _factory.StringLiteral(Unescape(part.ConstantValue.StringValue));
                    }

                    result = result == null ?
                             part :
                             _factory.Binary(BinaryOperatorKind.StringConcatenation, node.Type, result, part);
                }

                if (length == 1)
                {
                    result = _factory.Coalesce(result, _factory.StringLiteral(""));
                }
            }
            else
            {
                //
                // We lower an interpolated string into an invocation of String.Format.  For example, we translate the expression
                //
                //     $"Jenny don\'t change your number { 8675309 }"
                //
                // into
                //
                //     String.Format("Jenny don\'t change your number {0}", new object[] { 8675309 })
                //

                MakeInterpolatedStringFormat(node, out BoundExpression format, out ArrayBuilder <BoundExpression> expressions);

                // The normal pattern for lowering is to lower subtrees before the enclosing tree. However we cannot lower
                // the arguments first in this situation because we do not know what conversions will be
                // produced for the arguments until after we've done overload resolution. So we produce the invocation
                // and then lower it along with its arguments.
                expressions.Insert(0, format);
                var stringType = node.Type;
                result = _factory.StaticCall(stringType, "Format", expressions.ToImmutableAndFree(),
                                             allowUnexpandedForm: false // if an interpolation expression is the null literal, it should not match a params parameter.
                                             );
            }

            if (!result.HasAnyErrors)
            {
                result = VisitExpression(result); // lower the arguments AND handle expanded form, argument conversions, etc.
                result = MakeImplicitConversion(result, node.Type);
            }
            return(result);
        }
 /// <summary>
 /// Rewrites the given interpolated string to the set of handler creation and Append calls, returning an array builder of the append calls and the result
 /// local temp.
 /// </summary>
 /// <remarks>Caller is responsible for freeing the ArrayBuilder</remarks>
 private (ArrayBuilder <BoundExpression> HandlerPatternExpressions, BoundLocal Result) RewriteToInterpolatedStringHandlerPattern(BoundInterpolatedString node)
 {
     Debug.Assert(node.InterpolationData is { Construction: not null });
Пример #11
0
        public override BoundNode VisitInterpolatedString(BoundInterpolatedString node)
        {
            //
            // We lower an interpolated string into an invocation of String.Format.  For example, we translate the expression
            //
            //     "Jenny don\'t change your number \{ 8675309 }"
            //
            // into
            //
            //     String.Format("Jenny don\'t change your number {0}", new object[] { 8675309 })
            //

            //
            // TODO: A number of optimizations would be beneficial in the generated code.
            //
            // (1) Avoid the object array allocation by calling an overload of Format that has a fixed
            //     number of arguments. Check what is available in the platform and make the best choice,
            //     so that we benefit from any additional overloads that may be added in the future
            //
            // (2) If there is no width or format, and the argument is a value type, call .ToString()
            //     on it directly so that we avoid the boxing overhead.
            //
            // (3) For the built-in types, we can use .ToString(string format) for some format strings.
            //     Detect those cases that can be handled that way and take advantage of them.
            //

            int n            = node.Parts.Length - 1;
            var formatString = PooledStringBuilder.GetInstance();
            var fillins      = ArrayBuilder <BoundExpression> .GetInstance();

            int nextFormatPosition = 0;

            for (int i = 0; i <= n; i++)
            {
                var part = node.Parts[i];
                if ((i % 2) == 0)
                {
                    // this is one of the literal parts
                    foreach (var c in part.ConstantValue.StringValue)
                    {
                        // certain characters require escaping from String.Format
                        if (c == '{' || c == '}')
                        {
                            formatString.Builder.Append("{" + (nextFormatPosition++) + "}");
                            fillins.Add(factory.Convert(compilation.ObjectType, factory.StringLiteral(c.ToString())));
                        }
                        else
                        {
                            formatString.Builder.Append(c);
                        }
                    }
                }
                else
                {
                    // this is one of the expression holes
                    var fillin = (BoundStringInsert)part;
                    formatString.Builder.Append("{").Append(nextFormatPosition++);
                    if (fillin.Alignment != null && !fillin.Alignment.HasErrors)
                    {
                        formatString.Builder.Append(",").Append(fillin.Alignment.ConstantValue.Int64Value);
                    }
                    if (fillin.Format != null && !fillin.Format.HasErrors)
                    {
                        formatString.Builder.Append(":");
                        foreach (var c in fillin.Format.ConstantValue.StringValue)
                        {
                            // certain characters require escaping from String.Format
                            if (c == '{' || c == '}')
                            {
                                formatString.Builder.Append(c);
                            }
                            formatString.Builder.Append(c);
                        }
                    }
                    formatString.Builder.Append("}");
                    fillins.Add(VisitExpression(fillin.Value));
                }
            }

            var fillinParamsArray = factory.Array(compilation.ObjectType, fillins.ToImmutableAndFree());

            return(factory.StaticCall(node.Type, "Format", new BoundExpression[] { factory.StringLiteral(formatString.ToStringAndFree()), fillinParamsArray }));
        }
Пример #12
0
 protected override Conversion GetInterpolatedStringConversion(BoundInterpolatedString source, TypeSymbol destination, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo)
 {
     // Conversions involving interpolated strings require a Binder.
     throw ExceptionUtilities.Unreachable;
 }