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