diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs index 62ac0d538b0b0..9dbcb2383016a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs @@ -46,8 +46,8 @@ private BoundExpression RewriteStringConcatenation(SyntaxNode syntax, BinaryOper } // Convert both sides to a string (calling ToString if necessary) - loweredLeft = ConvertConcatExprToString(syntax, loweredLeft); - loweredRight = ConvertConcatExprToString(syntax, loweredRight); + loweredLeft = ConvertConcatExprToString(loweredLeft); + loweredRight = ConvertConcatExprToString(loweredRight); Debug.Assert(loweredLeft.Type is { } && (loweredLeft.Type.IsStringType() || loweredLeft.Type.IsErrorType()) || loweredLeft.ConstantValueOpt?.IsNull == true); Debug.Assert(loweredRight.Type is { } && (loweredRight.Type.IsStringType() || loweredRight.Type.IsErrorType()) || loweredRight.ConstantValueOpt?.IsNull == true); @@ -366,8 +366,10 @@ private BoundExpression RewriteStringConcatInExpressionLambda(SyntaxNode syntax, /// Returns an expression which converts the given expression into a string (or null). /// If necessary, this invokes .ToString() on the expression, to avoid boxing value types. /// - private BoundExpression ConvertConcatExprToString(SyntaxNode syntax, BoundExpression expr) + private BoundExpression ConvertConcatExprToString(BoundExpression expr) { + var syntax = expr.Syntax; + // If it's a value type, it'll have been boxed by the +(string, object) or +(object, string) // operator. Undo that. if (expr.Kind == BoundKind.Conversion) @@ -431,7 +433,7 @@ private BoundExpression ConvertConcatExprToString(SyntaxNode syntax, BoundExpres // types to all special value types. if (structToStringMethod != null && (expr.Type.SpecialType != SpecialType.None && !isFieldOfMarshalByRef(expr, _compilation))) { - return BoundCall.Synthesized(expr.Syntax, expr, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, structToStringMethod); + return BoundCall.Synthesized(syntax, expr, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, structToStringMethod); } // - It's a reference type (excluding unconstrained generics): no copy @@ -454,9 +456,9 @@ private BoundExpression ConvertConcatExprToString(SyntaxNode syntax, BoundExpres { if (!callWithoutCopy) { - expr = new BoundPassByCopy(expr.Syntax, expr, expr.Type); + expr = new BoundPassByCopy(syntax, expr, expr.Type); } - return BoundCall.Synthesized(expr.Syntax, expr, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, objectToStringMethod); + return BoundCall.Synthesized(syntax, expr, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, objectToStringMethod); } if (callWithoutCopy) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index e477803077b41..671144cdb90c9 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -2403,7 +2403,69 @@ static void Main() compilation.VerifyEmitDiagnostics( // (9,27): error CS0656: Missing compiler required member 'System.Object.ToString' // Console.WriteLine(c + "3"); - Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"c + ""3""").WithArguments("System.Object", "ToString").WithLocation(9, 27) + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(9, 27) + ); + } + + [Fact] + public void System_Object__ToString_3Args() + { + var source = """ + using System; + + public class Test + { + static void Main() + { + char c = 'c'; + int i = 2; + Console.WriteLine(c + "3" + i); + } + } + """; + + var compilation = CreateCompilationWithMscorlib45(source); + compilation.MakeMemberMissing(SpecialMember.System_Object__ToString); + compilation.VerifyEmitDiagnostics( + // (9,27): error CS0656: Missing compiler required member 'System.Object.ToString' + // Console.WriteLine(c + "3" + i); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(9, 27), + // (9,37): error CS0656: Missing compiler required member 'System.Object.ToString' + // Console.WriteLine(c + "3" + i); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "i").WithArguments("System.Object", "ToString").WithLocation(9, 37) + ); + } + + [Fact] + public void System_Object__ToString_4Args() + { + var source = """ + using System; + + public class Test + { + static void Main() + { + char c = 'c'; + int i = 2; + double d = 0.7; + Console.WriteLine(c + "3" + i + d); + } + } + """; + + var compilation = CreateCompilationWithMscorlib45(source); + compilation.MakeMemberMissing(SpecialMember.System_Object__ToString); + compilation.VerifyEmitDiagnostics( + // (10,27): error CS0656: Missing compiler required member 'System.Object.ToString' + // Console.WriteLine(c + "3" + i + d); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "c").WithArguments("System.Object", "ToString").WithLocation(10, 27), + // (10,37): error CS0656: Missing compiler required member 'System.Object.ToString' + // Console.WriteLine(c + "3" + i + d); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "i").WithArguments("System.Object", "ToString").WithLocation(10, 37), + // (10,41): error CS0656: Missing compiler required member 'System.Object.ToString' + // Console.WriteLine(c + "3" + i + d); + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "d").WithArguments("System.Object", "ToString").WithLocation(10, 41) ); }