Addressed remaining test items

All known outstanding tests are added. Also addressed all known
outstanding work items.
上级 433ff025
......@@ -3536,6 +3536,14 @@ private BoundExpression BindNullCoalescingAssignmentOperator(AssignmentExpressio
return GenerateNullCoalescingAssignmentBadBinaryOpsError(node, leftOperand, rightOperand, diagnostics);
}
// If the type of the left is Nullable<A0>, and the right is the default literal, the default will be
// converted to the default of Nullable<A0>, null, and not the default of A0. This is likely unintended
// on the part of the user, so we warn
if (leftType.IsNullableType() && rightOperand.IsLiteralDefault())
{
Error(diagnostics, ErrorCode.WRN_DefaultLiteralConvertedToNullIsNotIntended, node.Right, leftType.GetNullableUnderlyingType());
}
// If an implicit conversion exists from B to A, we store that conversion. At runtime, a is first evaluated. If
// a is not null, b is not evaluated. If a is null, b is evaluated and converted to type A, and is stored in a.
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
......
......@@ -12986,6 +12986,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to &apos;default&apos; is converted to &apos;null&apos;, not &apos;default({0})&apos;.
/// </summary>
internal static string WRN_DefaultLiteralConvertedToNullIsNotIntended {
get {
return ResourceManager.GetString("WRN_DefaultLiteralConvertedToNullIsNotIntended", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The default value specified for parameter &apos;{0}&apos; will have no effect because it applies to a member that is used in contexts that do not allow optional arguments.
/// </summary>
......
......@@ -5360,4 +5360,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="CannotCreateConstructedFromNongeneric" xml:space="preserve">
<value>Cannot create constructed generic type from non-generic type.</value>
</data>
<data name="WRN_DefaultLiteralConvertedToNullIsNotIntended" xml:space="preserve">
<value>'default' is converted to 'null', not 'default({0})'</value>
</data>
</root>
\ No newline at end of file
......@@ -1582,6 +1582,7 @@ internal enum ErrorCode
#region diagnostics introduced for C# 8.0
ERR_FeatureNotAvailableInVersion8 = 8400,
ERR_AltInterpolatedVerbatimStringsNotAvailable = 8401,
WRN_DefaultLiteralConvertedToNullIsNotIntended = 8402,
#endregion diagnostics introduced for C# 8.0
}
// Note: you will need to re-generate compiler code after adding warnings (build\scripts\generate-compiler-code.cmd)
......
......@@ -180,6 +180,7 @@ public static bool IsWarning(ErrorCode code)
case ErrorCode.WRN_AttributesOnBackingFieldsNotAvailable:
case ErrorCode.WRN_TupleBinopLiteralNameMismatch:
case ErrorCode.WRN_TypeParameterSameAsOuterMethodTypeParameter:
case ErrorCode.WRN_DefaultLiteralConvertedToNullIsNotIntended:
return true;
default:
return false;
......
......@@ -51,17 +51,20 @@ public override BoundNode VisitNullCoalescingOperator(BoundNullCoalescingOperato
// can simply generate "right". If it is not null then we can simply generate
// MakeConversion(left). This does not hold when the left is an unconstrained type parameter: at runtime,
// it can be either left or right depending on the runtime type of T
if (rewrittenLeft.IsDefaultValue() && !isUnconstrainedTypeParameter)
if (!isUnconstrainedTypeParameter)
{
if (rewrittenLeft.IsDefaultValue())
{
return rewrittenRight;
}
if (rewrittenLeft.ConstantValue != null && !isUnconstrainedTypeParameter)
if (rewrittenLeft.ConstantValue != null)
{
Debug.Assert(!rewrittenLeft.ConstantValue.IsNull);
return GetConvertedLeftForNullCoalescingOperator(rewrittenLeft, leftConversion, rewrittenResultType);
}
}
// string concatenation is never null.
// interpolated string lowering may introduce redundant null coalescing, which we have to remove.
......
......@@ -8714,6 +8714,7 @@ internal static bool IsRightAssociative(SyntaxKind op)
case SyntaxKind.OrAssignmentExpression:
case SyntaxKind.LeftShiftAssignmentExpression:
case SyntaxKind.RightShiftAssignmentExpression:
case SyntaxKind.CoalesceAssignmentExpression:
case SyntaxKind.CoalesceExpression:
return true;
default:
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -422,6 +422,11 @@
<target state="new">SyntaxTree is not part of the compilation, so it cannot be removed</target>
<note />
</trans-unit>
<trans-unit id="WRN_DefaultLiteralConvertedToNullIsNotIntended">
<source>'default' is converted to 'null', not 'default({0})'</source>
<target state="new">'default' is converted to 'null', not 'default({0})'</target>
<note />
</trans-unit>
<trans-unit id="WRN_TypeParameterSameAsOuterMethodTypeParameter">
<source>Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</source>
<target state="new">Type parameter '{0}' has the same name as the type parameter from outer method '{1}'</target>
......
......@@ -1384,6 +1384,13 @@ static void Goo<T>(T x1, T x2)
{
Console.WriteLine(x1 ?? x2);
}
static void Goo2<T1, T2>(T1 t1, T2 t2, dynamic d) where T1 : T2
{
// Verifying no type errors
T2 t = t1 ?? t2;
dynamic d2 = t1 ?? d;
}
}
";
var comp = CompileAndVerify(source, expectedOutput: @"
......
......@@ -131,5 +131,35 @@ void assertTypeInfo(SyntaxNode syntax)
}
}
[Fact]
public void CoalesceAssignment_NotConvertedToNonNullable()
{
var source = @"
class C
{
void M(int? a, int b)
{
a ??= b;
}
}";
var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8);
comp.VerifyDiagnostics();
var syntaxTree = comp.SyntaxTrees.Single();
var syntaxRoot = syntaxTree.GetRoot();
var semanticModel = comp.GetSemanticModel(syntaxTree);
var coalesceAssignment = syntaxRoot.DescendantNodes().OfType<AssignmentExpressionSyntax>().Single();
var nullable = comp.GetSpecialType(SpecialType.System_Nullable_T);
var int32 = comp.GetSpecialType(SpecialType.System_Int32);
var coalesceType = semanticModel.GetTypeInfo(coalesceAssignment).Type;
var genericParameter = ((INamedTypeSymbol)coalesceType).TypeArguments.Single();
Assert.Equal(nullable, coalesceType.OriginalDefinition);
Assert.Equal(int32, genericParameter);
}
}
}
......@@ -3944,6 +3944,33 @@ public void NullCoalesingAssigmentExpressionAndCoalescingOperator()
EOF();
}
[Fact]
public void NullCoalescingAssignmentExpressionNested()
{
UsingExpression("a ??= b ??= c");
N(SyntaxKind.CoalesceAssignmentExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "a");
}
N(SyntaxKind.QuestionQuestionEqualsToken);
N(SyntaxKind.CoalesceAssignmentExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "b");
}
N(SyntaxKind.QuestionQuestionEqualsToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "c");
}
}
}
EOF();
}
[Fact]
public void NullCoalescingAssignmentCSharp7_3()
{
......@@ -3951,6 +3978,19 @@ public void NullCoalescingAssignmentCSharp7_3()
// (1,3): error CS8370: Feature 'coalescing assignment' is not available in C# 7.3. Please use language version 8.0 or greater.
// a ??= b
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7_3, "??=").WithArguments("coalescing assignment", "8.0").WithLocation(1, 3));
N(SyntaxKind.CoalesceAssignmentExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "a");
}
N(SyntaxKind.QuestionQuestionEqualsToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "b");
}
}
EOF();
}
}
}
......@@ -2858,7 +2858,7 @@ public override IOperation VisitCoalesceAssignment(ICoalesceAssignmentOperation
SpillEvalStack();
int valueCaptureId = GetNextCaptureId(valueFrame.RegionBuilderOpt);
AddStatement(new FlowCapture(valueCaptureId, locationCapture.Syntax, locationCapture));
IOperation valueCapture = GetCaptureReference(valueCaptureId, OperationCloner.CloneOperation(locationCapture));
IOperation valueCapture = GetCaptureReference(valueCaptureId, locationCapture);
var whenNull = new BasicBlockBuilder(BasicBlockKind.Block);
var afterCoalesce = new BasicBlockBuilder(BasicBlockKind.Block);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册