Optimize BindInterpolatedString for the empty string cases, fix up tests.

上级 8f123bc6
......@@ -16,117 +16,122 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta
{
var builder = ArrayBuilder<BoundExpression>.GetInstance();
var stringType = GetSpecialType(SpecialType.System_String, diagnostics, node);
var objectType = GetSpecialType(SpecialType.System_Object, diagnostics, node);
var intType = GetSpecialType(SpecialType.System_Int32, diagnostics, node);
ConstantValue? resultConstant = null;
bool isResultConstant = true;
foreach (var content in node.Contents)
if (node.Contents.Count == 0)
{
switch (content.Kind())
resultConstant = ConstantValue.Create(string.Empty);
}
else
{
var objectType = GetSpecialType(SpecialType.System_Object, diagnostics, node);
var intType = GetSpecialType(SpecialType.System_Int32, diagnostics, node);
foreach (var content in node.Contents)
{
case SyntaxKind.Interpolation:
{
var interpolation = (InterpolationSyntax)content;
var value = BindValue(interpolation.Expression, diagnostics, BindValueKind.RValue);
if (value.Type is null)
{
value = GenerateConversionForAssignment(objectType, value, diagnostics);
}
else
switch (content.Kind())
{
case SyntaxKind.Interpolation:
{
value = BindToNaturalType(value, diagnostics);
_ = GenerateConversionForAssignment(objectType, value, diagnostics);
}
// We need to ensure the argument is not a lambda, method group, etc. It isn't nice to wait until lowering,
// when we perform overload resolution, to report a problem. So we do that check by calling
// GenerateConversionForAssignment with objectType. However we want to preserve the original expression's
// natural type so that overload resolution may select a specialized implementation of string.Format,
// so we discard the result of that call and only preserve its diagnostics.
BoundExpression? alignment = null;
BoundLiteral? format = null;
if (interpolation.AlignmentClause != null)
{
alignment = GenerateConversionForAssignment(intType, BindValue(interpolation.AlignmentClause.Value, diagnostics, Binder.BindValueKind.RValue), diagnostics);
var alignmentConstant = alignment.ConstantValue;
if (alignmentConstant != null && !alignmentConstant.IsBad)
var interpolation = (InterpolationSyntax)content;
var value = BindValue(interpolation.Expression, diagnostics, BindValueKind.RValue);
if (value.Type is null)
{
const int magnitudeLimit = 32767;
// check that the magnitude of the alignment is "in range".
int alignmentValue = alignmentConstant.Int32Value;
// We do the arithmetic using negative numbers because the largest negative int has no corresponding positive (absolute) value.
alignmentValue = (alignmentValue > 0) ? -alignmentValue : alignmentValue;
if (alignmentValue < -magnitudeLimit)
{
diagnostics.Add(ErrorCode.WRN_AlignmentMagnitude, alignment.Syntax.Location, alignmentConstant.Int32Value, magnitudeLimit);
}
value = GenerateConversionForAssignment(objectType, value, diagnostics);
}
else if (!alignment.HasErrors)
else
{
diagnostics.Add(ErrorCode.ERR_ConstantExpected, interpolation.AlignmentClause.Value.Location);
value = BindToNaturalType(value, diagnostics);
_ = GenerateConversionForAssignment(objectType, value, diagnostics);
}
}
if (interpolation.FormatClause != null)
{
var text = interpolation.FormatClause.FormatStringToken.ValueText;
char lastChar;
bool hasErrors = false;
if (text.Length == 0)
// We need to ensure the argument is not a lambda, method group, etc. It isn't nice to wait until lowering,
// when we perform overload resolution, to report a problem. So we do that check by calling
// GenerateConversionForAssignment with objectType. However we want to preserve the original expression's
// natural type so that overload resolution may select a specialized implementation of string.Format,
// so we discard the result of that call and only preserve its diagnostics.
BoundExpression? alignment = null;
BoundLiteral? format = null;
if (interpolation.AlignmentClause != null)
{
diagnostics.Add(ErrorCode.ERR_EmptyFormatSpecifier, interpolation.FormatClause.Location);
hasErrors = true;
alignment = GenerateConversionForAssignment(intType, BindValue(interpolation.AlignmentClause.Value, diagnostics, Binder.BindValueKind.RValue), diagnostics);
var alignmentConstant = alignment.ConstantValue;
if (alignmentConstant != null && !alignmentConstant.IsBad)
{
const int magnitudeLimit = 32767;
// check that the magnitude of the alignment is "in range".
int alignmentValue = alignmentConstant.Int32Value;
// We do the arithmetic using negative numbers because the largest negative int has no corresponding positive (absolute) value.
alignmentValue = (alignmentValue > 0) ? -alignmentValue : alignmentValue;
if (alignmentValue < -magnitudeLimit)
{
diagnostics.Add(ErrorCode.WRN_AlignmentMagnitude, alignment.Syntax.Location, alignmentConstant.Int32Value, magnitudeLimit);
}
}
else if (!alignment.HasErrors)
{
diagnostics.Add(ErrorCode.ERR_ConstantExpected, interpolation.AlignmentClause.Value.Location);
}
}
else if (SyntaxFacts.IsWhitespace(lastChar = text[text.Length - 1]) || SyntaxFacts.IsNewLine(lastChar))
if (interpolation.FormatClause != null)
{
diagnostics.Add(ErrorCode.ERR_TrailingWhitespaceInFormatSpecifier, interpolation.FormatClause.Location);
hasErrors = true;
}
var text = interpolation.FormatClause.FormatStringToken.ValueText;
char lastChar;
bool hasErrors = false;
if (text.Length == 0)
{
diagnostics.Add(ErrorCode.ERR_EmptyFormatSpecifier, interpolation.FormatClause.Location);
hasErrors = true;
}
else if (SyntaxFacts.IsWhitespace(lastChar = text[text.Length - 1]) || SyntaxFacts.IsNewLine(lastChar))
{
diagnostics.Add(ErrorCode.ERR_TrailingWhitespaceInFormatSpecifier, interpolation.FormatClause.Location);
hasErrors = true;
}
format = new BoundLiteral(interpolation.FormatClause, ConstantValue.Create(text), stringType, hasErrors);
}
format = new BoundLiteral(interpolation.FormatClause, ConstantValue.Create(text), stringType, hasErrors);
}
builder.Add(new BoundStringInsert(interpolation, value, alignment, format, null));
if (value.ConstantValue == null ||
!(interpolation is { FormatClause: null, AlignmentClause: null }) ||
!(value.ConstantValue is { IsString: true, IsBad: false }))
{
isResultConstant = false;
builder.Add(new BoundStringInsert(interpolation, value, alignment, format, null));
if (!isResultConstant ||
value.ConstantValue == null ||
!(interpolation is { FormatClause: null, AlignmentClause: null }) ||
!(value.ConstantValue is { IsString: true, IsBad: false }))
{
isResultConstant = false;
continue;
}
resultConstant = (resultConstant is null)
? value.ConstantValue
: FoldStringConcatenation(BinaryOperatorKind.StringConcatenation, resultConstant, value.ConstantValue);
continue;
}
resultConstant = (resultConstant is null)
? value.ConstantValue
: FoldStringConcatenation(BinaryOperatorKind.StringConcatenation, resultConstant, value.ConstantValue);
continue;
}
case SyntaxKind.InterpolatedStringText:
{
var text = ((InterpolatedStringTextSyntax)content).TextToken.ValueText;
builder.Add(new BoundLiteral(content, ConstantValue.Create(text, SpecialType.System_String), stringType));
if (isResultConstant)
case SyntaxKind.InterpolatedStringText:
{
var constantVal = ConstantValue.Create(ConstantValueUtils.UnescapeInterpolatedStringLiteral(text), SpecialType.System_String);
resultConstant = (resultConstant is null)
? constantVal
: FoldStringConcatenation(BinaryOperatorKind.StringConcatenation, resultConstant, constantVal);
var text = ((InterpolatedStringTextSyntax)content).TextToken.ValueText;
builder.Add(new BoundLiteral(content, ConstantValue.Create(text, SpecialType.System_String), stringType));
if (isResultConstant)
{
var constantVal = ConstantValue.Create(ConstantValueUtils.UnescapeInterpolatedStringLiteral(text), SpecialType.System_String);
resultConstant = (resultConstant is null)
? constantVal
: FoldStringConcatenation(BinaryOperatorKind.StringConcatenation, resultConstant, constantVal);
}
continue;
}
continue;
}
default:
throw ExceptionUtilities.UnexpectedValue(content.Kind());
default:
throw ExceptionUtilities.UnexpectedValue(content.Kind());
}
}
}
if (!isResultConstant)
{
resultConstant = null;
}
else if (resultConstant == null)
{
Debug.Assert(node.Contents.Count == 0);
resultConstant = ConstantValue.Create(string.Empty);
if (!isResultConstant)
{
resultConstant = null;
}
}
Debug.Assert(isResultConstant == (resultConstant != null));
return new BoundInterpolatedString(node, builder.ToImmutableAndFree(), resultConstant, stringType);
}
}
......
......@@ -29,7 +29,7 @@ public void M()
}
";
string expectedOperationTree = @"
IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""""')
IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, Constant: """") (Syntax: '$""""')
Parts(0)
";
var expectedDiagnostics = DiagnosticDescription.None;
......@@ -477,7 +477,7 @@ public void M(string p)
Left:
IParameterReferenceOperation: p (OperationKind.ParameterReference, Type: System.String) (Syntax: 'p')
Right:
IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""""')
IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, Constant: """") (Syntax: '$""""')
Parts(0)
Next (Regular) Block[B2]
......
......@@ -3760,12 +3760,20 @@ void M2(string S1 = $""Testing"", object O = null, Namae N = null)
[Fact]
public void EmptyConstInterpolatedString()
{
CreateCompilation(@"
class C
CompileAndVerify(@"
public class C
{
const string s = $"""";
public const string s = $"""";
static void Main()
{
System.Console.WriteLine(s);
}
}
", parseOptions: TestOptions.RegularPreview).VerifyDiagnostics();
", parseOptions: TestOptions.RegularPreview, expectedOutput: "", symbolValidator: module =>
{
Assert.Equal(string.Empty, module.GlobalNamespace.GetTypeMember("C").GetField("s").ConstantValue);
});
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册