From 4f7bef27a4b3283cf07f46413b6d3f1204af541b Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 2 Jun 2017 13:22:47 -0700 Subject: [PATCH] Disallow unary and binary operators on default literal, except == and != (#19870) --- .../Portable/Binder/Binder_Operators.cs | 49 ++- .../Portable/CSharpResources.Designer.cs | 18 + .../CSharp/Portable/CSharpResources.resx | 6 + .../CSharp/Portable/Errors/ErrorCode.cs | 3 +- .../Semantic/Semantics/SemanticErrorTests.cs | 47 +- .../Semantics/TargetTypedDefaultTests.cs | 411 ++++++++++++++++-- 6 files changed, 477 insertions(+), 57 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 66a454ffcac..dcbd3fd96a8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -479,7 +479,6 @@ private BoundExpression BindSimpleBinaryOperator(BinaryExpressionSyntax node, Di bool leftNull = left.IsLiteralNull(); bool rightNull = right.IsLiteralNull(); bool isEquality = kind == BinaryOperatorKind.Equal || kind == BinaryOperatorKind.NotEqual; - if (isEquality && leftNull && rightNull) { return new BoundLiteral(node, ConstantValue.Create(kind == BinaryOperatorKind.Equal), GetSpecialType(SpecialType.System_Boolean, diagnostics, node)); @@ -625,6 +624,19 @@ private void ReportAssignmentOperatorError(AssignmentExpressionSyntax node, Diag private static void ReportBinaryOperatorError(ExpressionSyntax node, DiagnosticBag diagnostics, SyntaxToken operatorToken, BoundExpression left, BoundExpression right, LookupResultKind resultKind) { + if ((operatorToken.Kind() == SyntaxKind.EqualsEqualsToken || operatorToken.Kind() == SyntaxKind.ExclamationEqualsToken) && + (left.IsLiteralDefault() && right.IsLiteralDefault())) + { + Error(diagnostics, ErrorCode.ERR_AmbigBinaryOpsOnDefault, node, operatorToken.Text); + return; + } + + if (left.IsLiteralDefault() || right.IsLiteralDefault()) + { + Error(diagnostics, ErrorCode.ERR_BadOpOnNullOrDefault, node, operatorToken.Text, "default"); + return; + } + ErrorCode errorCode = resultKind == LookupResultKind.Ambiguous ? ErrorCode.ERR_AmbigBinaryOps : // Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' ErrorCode.ERR_BadBinaryOps; // Operator '{0}' cannot be applied to operands of type '{1}' and '{2}' @@ -1023,6 +1035,13 @@ private TypeSymbol GetBinaryOperatorErrorType(BinaryOperatorKind kind, Diagnosti private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution(BinaryOperatorKind kind, BoundExpression left, BoundExpression right, CSharpSyntaxNode node, DiagnosticBag diagnostics, out LookupResultKind resultKind, out ImmutableArray originalUserDefinedOperators) { + if (!IsDefaultLiteralAllowedInBinaryOperator(kind, left, right)) + { + resultKind = LookupResultKind.OverloadResolutionFailure; + originalUserDefinedOperators = default(ImmutableArray); + return default(BinaryOperatorAnalysisResult); + } + var result = BinaryOperatorOverloadResolutionResult.GetInstance(); HashSet useSiteDiagnostics = null; this.OverloadResolution.BinaryOperatorOverloadResolution(kind, left, right, result, ref useSiteDiagnostics); @@ -1073,6 +1092,19 @@ private BinaryOperatorAnalysisResult BinaryOperatorOverloadResolution(BinaryOper return possiblyBest; } + private bool IsDefaultLiteralAllowedInBinaryOperator(BinaryOperatorKind kind, BoundExpression left, BoundExpression right) + { + bool isEquality = kind == BinaryOperatorKind.Equal || kind == BinaryOperatorKind.NotEqual; + if (isEquality) + { + return !left.IsLiteralDefault() || !right.IsLiteralDefault(); + } + else + { + return !left.IsLiteralDefault() && !right.IsLiteralDefault(); + } + } + private UnaryOperatorAnalysisResult UnaryOperatorOverloadResolution( UnaryOperatorKind kind, BoundExpression operand, @@ -2180,12 +2212,12 @@ private BoundExpression BindUnaryOperatorCore(CSharpSyntaxNode node, string oper { UnaryOperatorKind kind = SyntaxKindToUnaryOperatorKind(node.Kind()); - bool isOperandTypeNull = operand.IsLiteralNull(); + bool isOperandTypeNull = operand.IsLiteralNull() || operand.IsLiteralDefault(); if (isOperandTypeNull) { // Dev10 does not allow unary prefix operators to be applied to the null literal // (or other typeless expressions). - Error(diagnostics, ErrorCode.ERR_BadUnaryOp, node, operatorText, operand.Display); + Error(diagnostics, ErrorCode.ERR_BadOpOnNullOrDefault, node, operatorText, operand.Display); } // If the operand is bad, avoid generating cascading errors. @@ -3236,7 +3268,16 @@ internal static ConstantValue GetAsOperatorConstantResult(TypeSymbol operandType private BoundExpression GenerateNullCoalescingBadBinaryOpsError(BinaryExpressionSyntax node, BoundExpression leftOperand, BoundExpression rightOperand, Conversion leftConversion, DiagnosticBag diagnostics) { - Error(diagnostics, ErrorCode.ERR_BadBinaryOps, node, SyntaxFacts.GetText(node.OperatorToken.Kind()), leftOperand.Display, rightOperand.Display); + + if (leftOperand.IsLiteralDefault() || rightOperand.IsLiteralDefault()) + { + Error(diagnostics, ErrorCode.ERR_BadOpOnNullOrDefault, node, node.OperatorToken.Text, "default"); + } + else + { + Error(diagnostics, ErrorCode.ERR_BadBinaryOps, node, SyntaxFacts.GetText(node.OperatorToken.Kind()), leftOperand.Display, rightOperand.Display); + } + return new BoundNullCoalescingOperator(node, leftOperand, rightOperand, leftConversion, CreateErrorType(), hasErrors: true); } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 6544acbdfb2..da44d82830c 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -304,6 +304,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to Operator '{0}' is ambiguous on operands 'default' and 'default'. + /// + internal static string ERR_AmbigBinaryOpsOnDefault { + get { + return ResourceManager.GetString("ERR_AmbigBinaryOpsOnDefault", resourceCulture); + } + } + /// /// Looks up a localized string similar to The call is ambiguous between the following methods or properties: '{0}' and '{1}'. /// @@ -1735,6 +1744,15 @@ internal class CSharpResources { } } + /// + /// Looks up a localized string similar to Operator '{0}' cannot be applied to operand '{1}'. + /// + internal static string ERR_BadOpOnNullOrDefault { + get { + return ResourceManager.GetString("ERR_BadOpOnNullOrDefault", resourceCulture); + } + } + /// /// Looks up a localized string similar to The parameter modifier 'out' cannot be used with 'this' . /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index bd36370f443..291b27861e9 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -426,6 +426,9 @@ Operator '{0}' cannot be applied to operand of type '{1}' + + Operator '{0}' cannot be applied to operand '{1}' + Keyword 'this' is not valid in a static property, static method, or static field initializer @@ -450,6 +453,9 @@ Operator '{0}' is ambiguous on operands of type '{1}' and '{2}' + + Operator '{0}' is ambiguous on operands 'default' and 'default' + Operator '{0}' is ambiguous on an operand of type '{1}' diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 92bb41d818f..5740c465258 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1481,11 +1481,12 @@ internal enum ErrorCode ERR_NoRefOutWhenRefOnly = 8308, ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 8309, - // Available = 8310, + ERR_BadOpOnNullOrDefault = 8310, ERR_BadDynamicMethodArgDefaultLiteral = 8311, ERR_DefaultLiteralNotValid = 8312, WRN_DefaultInSwitch = 8313, ERR_PatternWrongGenericTypeInVersion = 8314, + ERR_AmbigBinaryOpsOnDefault = 8315, #endregion diagnostics introduced for C# 7.1 } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs index c257c71c06d..236e49a106b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs @@ -1055,19 +1055,27 @@ object M() "; CreateStandardCompilation(text).VerifyDiagnostics( // (9,17): error CS0023: Operator '!' cannot be applied to operand of type 'object' - Diagnostic(ErrorCode.ERR_BadUnaryOp, "!q").WithArguments("!", "object"), - // (12,26): error CS0023: Operator '-' cannot be applied to operand of type '' - Diagnostic(ErrorCode.ERR_BadUnaryOp, "-null").WithArguments("-", ""), - // (13,19): error CS0023: Operator '!' cannot be applied to operand of type '' - Diagnostic(ErrorCode.ERR_BadUnaryOp, "!null").WithArguments("!", ""), - // (14,19): error CS0023: Operator '~' cannot be applied to operand of type '' - Diagnostic(ErrorCode.ERR_BadUnaryOp, "~null").WithArguments("~", ""), + // if (!q) // CS0023 + Diagnostic(ErrorCode.ERR_BadUnaryOp, "!q").WithArguments("!", "object").WithLocation(9, 17), + // (12,26): error CS8310: Operator '-' cannot be applied to operand '' + // object obj = -null; // CS0023 + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "-null").WithArguments("-", "").WithLocation(12, 26), + // (13,19): error CS8310: Operator '!' cannot be applied to operand '' + // obj = !null; // CS0023 + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "!null").WithArguments("!", "").WithLocation(13, 19), + // (14,19): error CS8310: Operator '~' cannot be applied to operand '' + // obj = ~null; // CS0023 + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "~null").WithArguments("~", "").WithLocation(14, 19), // (16,13): error CS0023: Operator '++' cannot be applied to operand of type 'object' - Diagnostic(ErrorCode.ERR_BadUnaryOp, "obj++").WithArguments("++", "object"), + // obj++; // CS0023 + Diagnostic(ErrorCode.ERR_BadUnaryOp, "obj++").WithArguments("++", "object").WithLocation(16, 13), // (17,13): error CS0023: Operator '--' cannot be applied to operand of type 'object' - Diagnostic(ErrorCode.ERR_BadUnaryOp, "--obj").WithArguments("--", "object"), - // (18,20): error CS0023: Operator '+' cannot be applied to operand of type '' - Diagnostic(ErrorCode.ERR_BadUnaryOp, "+null").WithArguments("+", "")); + // --obj; // CS0023 + Diagnostic(ErrorCode.ERR_BadUnaryOp, "--obj").WithArguments("--", "object").WithLocation(17, 13), + // (18,20): error CS8310: Operator '+' cannot be applied to operand '' + // return +null; // CS0023 + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "+null").WithArguments("+", "").WithLocation(18, 20) + ); } [WorkItem(539590, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539590")] @@ -1092,18 +1100,19 @@ public static void Main() } "; CreateStandardCompilation(text).VerifyDiagnostics( - // (6,19): error CS0023: Operator '!' cannot be applied to operand of type '' + // (6,19): error CS8310: Operator '!' cannot be applied to operand '' // bool? b = !null; // CS0023 - Diagnostic(ErrorCode.ERR_BadUnaryOp, "!null").WithArguments("!", ""), - // (7,18): error CS0023: Operator '~' cannot be applied to operand of type '' + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "!null").WithArguments("!", "").WithLocation(6, 19), + // (7,18): error CS8310: Operator '~' cannot be applied to operand '' // int? n = ~null; // CS0023 - Diagnostic(ErrorCode.ERR_BadUnaryOp, "~null").WithArguments("~", ""), - // (8,20): error CS0023: Operator '+' cannot be applied to operand of type '' + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "~null").WithArguments("~", "").WithLocation(7, 18), + // (8,20): error CS8310: Operator '+' cannot be applied to operand '' // float? f = +null; // CS0023 - Diagnostic(ErrorCode.ERR_BadUnaryOp, "+null").WithArguments("+", ""), - // (9,19): error CS0023: Operator '-' cannot be applied to operand of type '' + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "+null").WithArguments("+", "").WithLocation(8, 20), + // (9,19): error CS8310: Operator '-' cannot be applied to operand '' // long? u = -null; // CS0023 - Diagnostic(ErrorCode.ERR_BadUnaryOp, "-null").WithArguments("-", "")); + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "-null").WithArguments("-", "").WithLocation(9, 19) + ); } [WorkItem(539590, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539590")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs index d45d6a6c4fb..93eb17aa7b8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TargetTypedDefaultTests.cs @@ -321,6 +321,38 @@ static void M() ); } + [Fact] + public void BadUnaryOperator() + { + string source = @" +class C +{ + static void M() + { + var a = +default; + var b = -default; + var c = ~default; + var d = !default; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (6,17): error CS8310: Operator '+' cannot be applied to operand 'default' + // var a = +default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "+default").WithArguments("+", "default").WithLocation(6, 17), + // (7,17): error CS8310: Operator '-' cannot be applied to operand 'default' + // var b = -default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "-default").WithArguments("-", "default").WithLocation(7, 17), + // (8,17): error CS8310: Operator '~' cannot be applied to operand 'default' + // var c = ~default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "~default").WithArguments("~", "default").WithLocation(8, 17), + // (9,17): error CS8310: Operator '!' cannot be applied to operand 'default' + // var d = !default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "!default").WithArguments("!", "default").WithLocation(9, 17) + ); + } + [Fact] public void AssignmentToRefType() { @@ -711,14 +743,314 @@ class C static void Main() { int i = checked(default); - int j = checked(default + 4); - System.Console.Write($""{i} {j}""); + System.Console.Write($""{i}""); } } "; var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "0 4"); + CompileAndVerify(comp, expectedOutput: "0"); + } + + [Fact] + public void InChecked2() + { + string source = @" +class C +{ + static void Main() + { + int j = checked(default + 4); + System.Console.Write($""{j}""); + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (6,25): error CS8310: Operator '+' cannot be applied to operand 'default' + // int j = checked(default + 4); + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default + 4").WithArguments("+", "default").WithLocation(6, 25) + ); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); + + var addition = nodes.OfType().Single(); + Assert.Null(model.GetSymbolInfo(addition).Symbol); + } + + [Fact] + public void TestBinaryOperators() + { + string source = @" +class C +{ + static void Main() + { + var a = default + default; + var b = default - default; + var c = default & default; + var d = default | default; + var e = default ^ default; + var f = default * default; + var g = default / default; + var h = default % default; + var i = default >> default; + var j = default << default; + var k = default > default; + var l = default < default; + var m = default >= default; + var n = default <= default; + var o = default == default; // ambiguous + var p = default != default; // ambiguous + var q = default && default; + var r = default || default; + var s = default ?? default; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (6,17): error CS8310: Operator '+' cannot be applied to operand 'default' + // var a = default + default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default + default").WithArguments("+", "default").WithLocation(6, 17), + // (7,17): error CS8310: Operator '-' cannot be applied to operand 'default' + // var b = default - default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default - default").WithArguments("-", "default").WithLocation(7, 17), + // (8,17): error CS8310: Operator '&' cannot be applied to operand 'default' + // var c = default & default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default & default").WithArguments("&", "default").WithLocation(8, 17), + // (9,17): error CS8310: Operator '|' cannot be applied to operand 'default' + // var d = default | default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default | default").WithArguments("|", "default").WithLocation(9, 17), + // (10,17): error CS8310: Operator '^' cannot be applied to operand 'default' + // var e = default ^ default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default ^ default").WithArguments("^", "default").WithLocation(10, 17), + // (11,17): error CS8310: Operator '*' cannot be applied to operand 'default' + // var f = default * default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default * default").WithArguments("*", "default").WithLocation(11, 17), + // (12,17): error CS8310: Operator '/' cannot be applied to operand 'default' + // var g = default / default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default / default").WithArguments("/", "default").WithLocation(12, 17), + // (13,17): error CS8310: Operator '%' cannot be applied to operand 'default' + // var h = default % default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default % default").WithArguments("%", "default").WithLocation(13, 17), + // (14,17): error CS8310: Operator '>>' cannot be applied to operand 'default' + // var i = default >> default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default >> default").WithArguments(">>", "default").WithLocation(14, 17), + // (15,17): error CS8310: Operator '<<' cannot be applied to operand 'default' + // var j = default << default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default << default").WithArguments("<<", "default").WithLocation(15, 17), + // (16,17): error CS8310: Operator '>' cannot be applied to operand 'default' + // var k = default > default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default > default").WithArguments(">", "default").WithLocation(16, 17), + // (17,17): error CS8310: Operator '<' cannot be applied to operand 'default' + // var l = default < default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default < default").WithArguments("<", "default").WithLocation(17, 17), + // (18,17): error CS8310: Operator '>=' cannot be applied to operand 'default' + // var m = default >= default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default >= default").WithArguments(">=", "default").WithLocation(18, 17), + // (19,17): error CS8310: Operator '<=' cannot be applied to operand 'default' + // var n = default <= default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default <= default").WithArguments("<=", "default").WithLocation(19, 17), + // (20,17): error CS8315: Operator '==' is ambiguous on operands 'default' and 'default' + // var o = default == default; // ambiguous + Diagnostic(ErrorCode.ERR_AmbigBinaryOpsOnDefault, "default == default").WithArguments("==").WithLocation(20, 17), + // (21,17): error CS8315: Operator '!=' is ambiguous on operands 'default' and 'default' + // var p = default != default; // ambiguous + Diagnostic(ErrorCode.ERR_AmbigBinaryOpsOnDefault, "default != default").WithArguments("!=").WithLocation(21, 17), + // (22,17): error CS8310: Operator '&&' cannot be applied to operand 'default' + // var q = default && default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default && default").WithArguments("&&", "default").WithLocation(22, 17), + // (23,17): error CS8310: Operator '||' cannot be applied to operand 'default' + // var r = default || default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default || default").WithArguments("||", "default").WithLocation(23, 17), + // (24,17): error CS8310: Operator '??' cannot be applied to operand 'default' + // var s = default ?? default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default ?? default").WithArguments("??", "default").WithLocation(24, 17) + ); + } + + [Fact] + public void TestBinaryOperators2() + { + string source = @" +class C +{ + static void Main() + { + var a = default + 1; + var b = default - 1; + var c = default & 1; + var d = default | 1; + var e = default ^ 1; + var f = default * 1; + var g = default / 1; + var h = default % 1; + var i = default >> 1; + var j = default << 1; + var k = default > 1; + var l = default < 1; + var m = default >= 1; + var n = default <= 1; + var o = default == 1; // ok + var p = default != 1; // ok + var q = default && 1; + var r = default || 1; + var s = default ?? 1; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (6,17): error CS8310: Operator '+' cannot be applied to operand 'default' + // var a = default + 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default + 1").WithArguments("+", "default").WithLocation(6, 17), + // (7,17): error CS8310: Operator '-' cannot be applied to operand 'default' + // var b = default - 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default - 1").WithArguments("-", "default").WithLocation(7, 17), + // (8,17): error CS8310: Operator '&' cannot be applied to operand 'default' + // var c = default & 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default & 1").WithArguments("&", "default").WithLocation(8, 17), + // (9,17): error CS8310: Operator '|' cannot be applied to operand 'default' + // var d = default | 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default | 1").WithArguments("|", "default").WithLocation(9, 17), + // (10,17): error CS8310: Operator '^' cannot be applied to operand 'default' + // var e = default ^ 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default ^ 1").WithArguments("^", "default").WithLocation(10, 17), + // (11,17): error CS8310: Operator '*' cannot be applied to operand 'default' + // var f = default * 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default * 1").WithArguments("*", "default").WithLocation(11, 17), + // (12,17): error CS8310: Operator '/' cannot be applied to operand 'default' + // var g = default / 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default / 1").WithArguments("/", "default").WithLocation(12, 17), + // (13,17): error CS8310: Operator '%' cannot be applied to operand 'default' + // var h = default % 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default % 1").WithArguments("%", "default").WithLocation(13, 17), + // (14,17): error CS8310: Operator '>>' cannot be applied to operand 'default' + // var i = default >> 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default >> 1").WithArguments(">>", "default").WithLocation(14, 17), + // (15,17): error CS8310: Operator '<<' cannot be applied to operand 'default' + // var j = default << 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default << 1").WithArguments("<<", "default").WithLocation(15, 17), + // (16,17): error CS8310: Operator '>' cannot be applied to operand 'default' + // var k = default > 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default > 1").WithArguments(">", "default").WithLocation(16, 17), + // (17,17): error CS8310: Operator '<' cannot be applied to operand 'default' + // var l = default < 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default < 1").WithArguments("<", "default").WithLocation(17, 17), + // (18,17): error CS8310: Operator '>=' cannot be applied to operand 'default' + // var m = default >= 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default >= 1").WithArguments(">=", "default").WithLocation(18, 17), + // (19,17): error CS8310: Operator '<=' cannot be applied to operand 'default' + // var n = default <= 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default <= 1").WithArguments("<=", "default").WithLocation(19, 17), + // (22,17): error CS8310: Operator '&&' cannot be applied to operand 'default' + // var q = default && 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default && 1").WithArguments("&&", "default").WithLocation(22, 17), + // (23,17): error CS8310: Operator '||' cannot be applied to operand 'default' + // var r = default || 1; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default || 1").WithArguments("||", "default").WithLocation(23, 17), + // (20,13): warning CS0219: The variable 'o' is assigned but its value is never used + // var o = default == 1; // ok + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "o").WithArguments("o").WithLocation(20, 13), + // (21,13): warning CS0219: The variable 'p' is assigned but its value is never used + // var p = default != 1; // ok + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "p").WithArguments("p").WithLocation(21, 13) + ); + } + + [Fact] + public void TestBinaryOperators3() + { + string source = @" +class C +{ + static void Main() + { + var a = 1 + default; + var b = 1 - default; + var c = 1 & default; + var d = 1 | default; + var e = 1 ^ default; + var f = 1 * default; + var g = 1 / default; + var h = 1 % default; + var i = 1 >> default; + var j = 1 << default; + var k = 1 > default; + var l = 1 < default; + var m = 1 >= default; + var n = 1 <= default; + var o = 1 == default; // ok + var p = 1 != default; // ok + var q = 1 && default; + var r = 1 || default; + var s = 1 ?? default; + } +} +"; + var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1); + comp.VerifyDiagnostics( + // (6,17): error CS8310: Operator '+' cannot be applied to operand 'default' + // var a = 1 + default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 + default").WithArguments("+", "default").WithLocation(6, 17), + // (7,17): error CS8310: Operator '-' cannot be applied to operand 'default' + // var b = 1 - default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 - default").WithArguments("-", "default").WithLocation(7, 17), + // (8,17): error CS8310: Operator '&' cannot be applied to operand 'default' + // var c = 1 & default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 & default").WithArguments("&", "default").WithLocation(8, 17), + // (9,17): error CS8310: Operator '|' cannot be applied to operand 'default' + // var d = 1 | default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 | default").WithArguments("|", "default").WithLocation(9, 17), + // (10,17): error CS8310: Operator '^' cannot be applied to operand 'default' + // var e = 1 ^ default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 ^ default").WithArguments("^", "default").WithLocation(10, 17), + // (11,17): error CS8310: Operator '*' cannot be applied to operand 'default' + // var f = 1 * default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 * default").WithArguments("*", "default").WithLocation(11, 17), + // (12,17): error CS8310: Operator '/' cannot be applied to operand 'default' + // var g = 1 / default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 / default").WithArguments("/", "default").WithLocation(12, 17), + // (13,17): error CS8310: Operator '%' cannot be applied to operand 'default' + // var h = 1 % default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 % default").WithArguments("%", "default").WithLocation(13, 17), + // (14,17): error CS8310: Operator '>>' cannot be applied to operand 'default' + // var i = 1 >> default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 >> default").WithArguments(">>", "default").WithLocation(14, 17), + // (15,17): error CS8310: Operator '<<' cannot be applied to operand 'default' + // var j = 1 << default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 << default").WithArguments("<<", "default").WithLocation(15, 17), + // (16,17): error CS8310: Operator '>' cannot be applied to operand 'default' + // var k = 1 > default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 > default").WithArguments(">", "default").WithLocation(16, 17), + // (17,17): error CS8310: Operator '<' cannot be applied to operand 'default' + // var l = 1 < default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 < default").WithArguments("<", "default").WithLocation(17, 17), + // (18,17): error CS8310: Operator '>=' cannot be applied to operand 'default' + // var m = 1 >= default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 >= default").WithArguments(">=", "default").WithLocation(18, 17), + // (19,17): error CS8310: Operator '<=' cannot be applied to operand 'default' + // var n = 1 <= default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 <= default").WithArguments("<=", "default").WithLocation(19, 17), + // (22,17): error CS8310: Operator '&&' cannot be applied to operand 'default' + // var q = 1 && default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 && default").WithArguments("&&", "default").WithLocation(22, 17), + // (23,17): error CS8310: Operator '||' cannot be applied to operand 'default' + // var r = 1 || default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 || default").WithArguments("||", "default").WithLocation(23, 17), + // (24,17): error CS8310: Operator '??' cannot be applied to operand 'default' + // var s = 1 ?? default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "1 ?? default").WithArguments("??", "default").WithLocation(24, 17), + // (20,13): warning CS0219: The variable 'o' is assigned but its value is never used + // var o = 1 == default; // ok + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "o").WithArguments("o").WithLocation(20, 13), + // (21,13): warning CS0219: The variable 'p' is assigned but its value is never used + // var p = 1 != default; // ok + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "p").WithArguments("p").WithLocation(21, 13) + ); } [Fact] @@ -730,31 +1062,27 @@ struct S int field; static void Main() { - S s = new S(40) + default; - s += new S(2); + S s = new S(40); s += default; - System.Console.Write(s); } S(int i) { field = i; } public static S operator +(S left, S right) => new S(left.field + right.field); - public override string ToString() => field.ToString(); } "; var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); - comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "42"); + comp.VerifyDiagnostics( + // (8,9): error CS8310: Operator '+=' cannot be applied to operand 'default' + // s += default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "s += default").WithArguments("+=", "default").WithLocation(8, 9) + ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var nodes = tree.GetCompilationUnitRoot().DescendantNodes(); - var first = nodes.OfType().ElementAt(1); - Assert.Equal("new S(40) + default", first.Parent.ToString()); - Assert.Equal("S", model.GetTypeInfo(first).Type.ToTestDisplayString()); - - var second = nodes.OfType().ElementAt(3); - Assert.Equal("s += default", second.Parent.ToString()); - Assert.Equal("S", model.GetTypeInfo(second).Type.ToTestDisplayString()); + var defaultLiteral = nodes.OfType().ElementAt(1); + Assert.Equal("s += default", defaultLiteral.Parent.ToString()); + Assert.Null(model.GetTypeInfo(defaultLiteral).Type); } [Fact] @@ -842,6 +1170,15 @@ static void Main() "; var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); comp.VerifyDiagnostics( + // (9,13): error CS8310: Operator '+=' cannot be applied to operand 'default' + // i += default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "i += default").WithArguments("+=", "default").WithLocation(9, 13), + // (11,13): error CS8310: Operator '&=' cannot be applied to operand 'default' + // b &= default; + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "b &= default").WithArguments("&=", "default").WithLocation(11, 13), + // (12,37): error CS8310: Operator '|' cannot be applied to operand 'default' + // System.Console.Write($"{true | default} {i} {b}"); + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "true | default").WithArguments("|", "default").WithLocation(12, 37), // (15,40): warning CS7095: Filter expression is a constant, consider removing the filter // catch (System.Exception) when (default) Diagnostic(ErrorCode.WRN_FilterIsConstant, "default").WithLocation(15, 40), @@ -849,7 +1186,6 @@ static void Main() // System.Console.Write("catch"); Diagnostic(ErrorCode.WRN_UnreachableCode, "System").WithLocation(17, 13) ); - //CompileAndVerify(comp, expectedOutput: "True 2 False"); // PEVerify failed with Branch out of the method. Follow-up issue: https://github.com/dotnet/roslyn/issues/18678 } [Fact] @@ -939,21 +1275,24 @@ static void Main() { if (!default) { - System.Console.WriteLine(""reached""); + throw null; } } }"; var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); - comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "reached"); + comp.VerifyDiagnostics( + // (6,13): error CS8310: Operator '!' cannot be applied to operand 'default' + // if (!default) + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "!default").WithArguments("!", "default").WithLocation(6, 13) + ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var def = tree.GetCompilationUnitRoot().DescendantNodes().OfType().ElementAt(0); Assert.Equal("default", def.ToString()); - Assert.Equal("System.Boolean", model.GetTypeInfo(def).Type.ToTestDisplayString()); - Assert.Equal("System.Boolean", model.GetTypeInfo(def).ConvertedType.ToTestDisplayString()); + Assert.Null(model.GetTypeInfo(def).Type); + Assert.Null(model.GetTypeInfo(def).ConvertedType); } [Fact] @@ -973,10 +1312,10 @@ static void Main() comp.VerifyDiagnostics( // (6,13): error CS0023: Operator '!' cannot be applied to operand of type 'method group' // if (!Main || !null) - Diagnostic(ErrorCode.ERR_BadUnaryOp, "!Main").WithArguments("!", "method group"), - // (6,22): error CS0023: Operator '!' cannot be applied to operand of type '' + Diagnostic(ErrorCode.ERR_BadUnaryOp, "!Main").WithArguments("!", "method group").WithLocation(6, 13), + // (6,22): error CS8310: Operator '!' cannot be applied to operand '' // if (!Main || !null) - Diagnostic(ErrorCode.ERR_BadUnaryOp, "!null").WithArguments("!", "").WithLocation(6, 22) + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "!null").WithArguments("!", "").WithLocation(6, 22) ); } @@ -1381,8 +1720,11 @@ static void Main() } "; var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); - comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "0 1"); + comp.VerifyDiagnostics( + // (5,16): error CS8310: Operator '+' cannot be applied to operand 'default' + // OneEntry = default + 1 + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default + 1").WithArguments("+", "default").WithLocation(5, 16) + ); } [Fact] @@ -1403,8 +1745,11 @@ static void Main() } "; var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); - comp.VerifyDiagnostics(); - CompileAndVerify(comp, expectedOutput: "0 1"); + comp.VerifyDiagnostics( + // (5,16): error CS8310: Operator '+' cannot be applied to operand 'default' + // OneEntry = default + 1 + Diagnostic(ErrorCode.ERR_BadOpOnNullOrDefault, "default + 1").WithArguments("+", "default").WithLocation(5, 16) + ); } [Fact] @@ -1523,12 +1868,12 @@ static void Main() var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe); comp.VerifyDiagnostics( - // (6,33): error CS0034: Operator '==' is ambiguous on operands of type 'default' and 'default' + // (6,33): error CS8315: Operator '==' is ambiguous on operands 'default' and 'default' // System.Console.Write($"{default == default} {default != default}"); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "default == default").WithArguments("==", "default", "default").WithLocation(6, 33), - // (6,54): error CS0034: Operator '!=' is ambiguous on operands of type 'default' and 'default' + Diagnostic(ErrorCode.ERR_AmbigBinaryOpsOnDefault, "default == default").WithArguments("==").WithLocation(6, 33), + // (6,54): error CS8315: Operator '!=' is ambiguous on operands 'default' and 'default' // System.Console.Write($"{default == default} {default != default}"); - Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "default != default").WithArguments("!=", "default", "default").WithLocation(6, 54) + Diagnostic(ErrorCode.ERR_AmbigBinaryOpsOnDefault, "default != default").WithArguments("!=").WithLocation(6, 54) ); } -- GitLab