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 { });
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); }
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()); }
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 });
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 })); }
protected override Conversion GetInterpolatedStringConversion(BoundInterpolatedString source, TypeSymbol destination, ref CompoundUseSiteInfo <AssemblySymbol> useSiteInfo) { // Conversions involving interpolated strings require a Binder. throw ExceptionUtilities.Unreachable; }