예제 #1
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((TypeSymbol.Equals(destination, Compilation.GetWellKnownType(WellKnownType.core_IFormattable), TypeCompareKind.ConsiderEverything2) ||
             TypeSymbol.Equals(destination, Compilation.GetWellKnownType(WellKnownType.core_FormattableString), TypeCompareKind.ConsiderEverything2))
         ? Conversion.InterpolatedString : Conversion.NoConversion);
 }
예제 #2
0
 public override BoundNode VisitInterpolatedString(BoundInterpolatedString node)
 {
     Binder.CheckFeatureAvailability(
         node.Syntax,
         MessageID.IDS_FeatureConstantInterpolatedStrings,
         diagnostics
         );
     return(null);
 }
        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 = MakeImplicitConversion(result, node.Type);
            }
            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());
        }
예제 #5
0
        private 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("}");
                    var value = fillin.Value;
                    if (value.Type?.TypeKind == TypeKind.Dynamic)
                    {
                        value = MakeConversion(value, _compilation.ObjectType, @checked: false);
                    }

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

            format = _factory.StringLiteral(formatString.ToStringAndFree());
        }
예제 #7
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;
 }
예제 #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);
        }
예제 #10
0
 protected override Conversion GetInterpolatedStringConversion(BoundInterpolatedString source, TypeSymbol destination, ref HashSet <DiagnosticInfo> useSiteDiagnostics)
 {
     // Conversions involving interpolated strings require a Binder.
     throw ExceptionUtilities.Unreachable;
 }