diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs index 43cefebc3f115..1dfd1d1c43af2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs @@ -192,19 +192,7 @@ private TypeSymbol GetAnonymousTypeFieldType(BoundExpression expression, CSharpS } else { - if (expression.Kind == BoundKind.UnboundLambda) - { - errorArg = ((UnboundLambda)expression).MessageID.Localize(); - } - else if (expression.Kind == BoundKind.MethodGroup) - { - errorArg = MessageID.IDS_MethodGroup.Localize(); - } - else - { - Debug.Assert(expression.IsLiteralNull(), "How did we successfully bind an expression without a type?"); - errorArg = MessageID.IDS_NULL.Localize(); - } + errorArg = expression.Display; } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 91b432bf77bf7..f59d9cb713db4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -50,6 +50,7 @@ internal BoundPattern BindPattern( return new BoundWildcardPattern(node); default: + // PROTOTYPE: Can this occur due to parser error recovery? If so, how to handle? throw ExceptionUtilities.UnexpectedValue(node.Kind()); } } @@ -64,11 +65,10 @@ private BoundPattern BindRecursivePattern( var type = (NamedTypeSymbol)this.BindType(node.Type, diagnostics); hasErrors = hasErrors || CheckValidPatternType(node.Type, operand, operandType, type, false, diagnostics); - // We intend that (positional) recursive pattern-matching should be defined in terms of - // a pattern of user-defined methods or operators. Tentatively, perhaps a method called - // GetValues that has an out parameter for each position of the recursive pattern. But - // for now we try to *infer* a positional pattern-matching operation from the presence of - // an accessible constructor. + // PROTOTYPE: We intend that (positional) recursive pattern-matching should be defined in terms of + // PROTOTYPE: a pattern of user-defined methods or operators. As currently specified it is `operator is`. + // PROTOTYPE: But for now we try to *infer* a positional pattern-matching operation from the presence of + // PROTOTYPE: an accessible constructor. var correspondingMembers = default(ImmutableArray); HashSet useSiteDiagnostics = null; var memberNames = type.MemberNames; @@ -112,6 +112,7 @@ private BoundPattern BindRecursivePattern( if (!correspondingMembersForCtor.SequenceEqual(correspondingMembers, (s1, s2) => s1 == s2)) { correspondingMembersForCtor.Free(); + // PROTOTYPE: If we decide to support this, we need a properly i18n'd diagnostic. Error(diagnostics, ErrorCode.ERR_FeatureIsUnimplemented, node, "cannot infer a positional pattern from conflicting constructors"); diagnostics.Add(node, useSiteDiagnostics); @@ -124,6 +125,7 @@ private BoundPattern BindRecursivePattern( if (correspondingMembers == null) { + // PROTOTYPE: If we decide to support this, we need a properly i18n'd diagnostic. Error(diagnostics, ErrorCode.ERR_FeatureIsUnimplemented, node, "cannot infer a positional pattern from any accessible constructor"); diagnostics.Add(node, useSiteDiagnostics); @@ -260,6 +262,7 @@ private BoundExpression BindPropertyPatternMember( bool hasErrors = boundMember.HasAnyErrors || implicitReceiver.HasAnyErrors; + // PROTOTYPE: the following may be needed if we end up supporting an indexed property. //ImmutableArray arguments = ImmutableArray.Empty; //ImmutableArray argumentNamesOpt = default(ImmutableArray); //ImmutableArray argsToParamsOpt = default(ImmutableArray); @@ -273,8 +276,9 @@ private BoundExpression BindPropertyPatternMember( break; case BoundKind.IndexerAccess: - // TODO: Should a property pattern be capable of referencing an indexed property? - // https://github.com/dotnet/roslyn/issues/9375 + // PROTOTYPE: Should a property pattern be capable of referencing an indexed property? + // PROTOTYPE: This is an open issue in the language specification + // PROTOTYPE: See https://github.com/dotnet/roslyn/issues/9375 //{ // var indexer = (BoundIndexerAccess)boundMember; // arguments = indexer.Arguments; @@ -286,8 +290,9 @@ private BoundExpression BindPropertyPatternMember( //} case BoundKind.DynamicIndexerAccess: - // TODO: Should a property pattern be capable of referencing a dynamic indexer? - // https://github.com/dotnet/roslyn/issues/9375 + // PROTOTYPE: Should a property pattern be capable of referencing a dynamic indexer? + // PROTOTYPE: This is an open issue in the language specification + // PROTOTYPE: See https://github.com/dotnet/roslyn/issues/9375 //{ // var indexer = (BoundDynamicIndexerAccess)boundMember; // arguments = indexer.Arguments; @@ -297,8 +302,9 @@ private BoundExpression BindPropertyPatternMember( //} case BoundKind.EventAccess: - // TODO: Should a property pattern be capable of referencing an event? - // https://github.com/dotnet/roslyn/issues/9515 + // PROTOTYPE: Should a property pattern be capable of referencing an event? + // PROTOTYPE: This is an open issue in the language specification + // PROTOTYPE: See https://github.com/dotnet/roslyn/issues/9515 default: return BadSubpatternMemberAccess(boundMember, implicitReceiver, memberName, diagnostics, hasErrors); @@ -345,14 +351,14 @@ private BoundExpression BadSubpatternMemberAccess( break; default: - // TODO: review and test this code path. + // PROTOTYPE: We need to review and test this code path. // https://github.com/dotnet/roslyn/issues/9542 Error(diagnostics, ErrorCode.ERR_PropertyLacksGet, memberName, member); break; } } - // TODO: review and test this code path. Is LookupResultKind.Inaccessible appropriate? + // PROTOTYPE: review and test this code path. Is LookupResultKind.Inaccessible appropriate? // https://github.com/dotnet/roslyn/issues/9542 return ToBadExpression(boundMember, LookupResultKind.Inaccessible); } @@ -371,7 +377,7 @@ private BoundPattern BindConstantPattern( hasErrors = true; } - // TODO: check that the constant is valid for the given operand or operandType. + // PROTOTYPE: we still need to check that the constant is valid for the given operand or operandType. return new BoundConstantPattern(node, expression, hasErrors); } @@ -411,7 +417,7 @@ private bool CheckValidPatternType( { case ConversionKind.Boxing: case ConversionKind.ExplicitNullable: - case ConversionKind.ExplicitNumeric: // TODO: we should constrain this to integral? + case ConversionKind.ExplicitNumeric: // PROTOTYPE: we should constrain this to integral? Need LDM decision case ConversionKind.ExplicitReference: case ConversionKind.Identity: case ConversionKind.ImplicitReference: @@ -548,7 +554,7 @@ private BoundExpression BindMatchExpression( DiagnosticBag diagnostics) { var expression = BindValue(node.Left, diagnostics, BindValueKind.RValue); - // TODO: any constraints on a switch expression must be enforced here. For example, + // PROTOTYPE: any constraints on a switch expression must be enforced here. For example, // it must have a type (not be target-typed, lambda, null, etc) var sectionBuilder = ArrayBuilder.GetInstance(); @@ -572,30 +578,40 @@ private BoundExpression BindThrowExpression(ThrowExpressionSyntax node, Diagnost { switch (node.Parent.Kind()) { - case SyntaxKind.ConditionalExpression: + case SyntaxKind.ConditionalExpression: // ?: { var papa = (ConditionalExpressionSyntax)node.Parent; if (node == papa.WhenTrue || node == papa.WhenFalse) goto syntaxOk; break; } - case SyntaxKind.CoalesceExpression: + case SyntaxKind.CoalesceExpression: // ?? { var papa = (BinaryExpressionSyntax)node.Parent; if (node == papa.Right) goto syntaxOk; break; } - case SyntaxKind.MatchSection: + case SyntaxKind.MatchSection: // match { var papa = (MatchSectionSyntax)node.Parent; if (node == papa.Expression) goto syntaxOk; break; } - case SyntaxKind.ArrowExpressionClause: + case SyntaxKind.ArrowExpressionClause: // => { var papa = (ArrowExpressionClauseSyntax)node.Parent; if (node == papa.Expression) goto syntaxOk; break; } + // PROTOTYPE: It has been suggested that we allow throw on the right of && and ||, but + // PROTOTYPE: due to the relative precedence the parser already rejects that. + //case SyntaxKind.LogicalAndExpression: // && + //case SyntaxKind.LogicalOrExpression: // || + // { + // // PROTOTYPE: it isn't clear what the semantics should be + // var papa = (BinaryExpressionSyntax)node.Parent; + // if (node == papa.Right) goto syntaxOk; + // break; + // } default: break; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs index 9bf48e03051f0..ea91e2fb3c6ed 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs @@ -568,21 +568,7 @@ private void ReduceLet(LetClauseSyntax let, QueryTranslationState state, Diagnos SourceLocation errorLocation = new SourceLocation(let.SyntaxTree, new TextSpan(let.Identifier.SpanStart, let.Expression.Span.End - let.Identifier.SpanStart)); if (!yExpression.HasAnyErrors && !yExpression.HasExpressionType()) { - MessageID id = MessageID.IDS_NULL; - if (yExpression.Kind == BoundKind.UnboundLambda) - { - id = ((UnboundLambda)yExpression).MessageID; - } - else if (yExpression.Kind == BoundKind.MethodGroup) - { - id = MessageID.IDS_MethodGroup; - } - else - { - Debug.Assert(yExpression.IsLiteralNull(), "How did we successfully bind an expression without a type?"); - } - - Error(d, ErrorCode.ERR_QueryRangeVariableAssignedBadValue, errorLocation, id.Localize()); + Error(d, ErrorCode.ERR_QueryRangeVariableAssignedBadValue, errorLocation, yExpression.Display); yExpression = new BoundBadExpression(yExpression.Syntax, LookupResultKind.Empty, ImmutableArray.Empty, ImmutableArray.Create(yExpression), CreateErrorType()); } else if (!yExpression.HasAnyErrors && yExpression.Type.SpecialType == SpecialType.System_Void) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index ffdf1365030c3..1dd0174f533ec 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -692,22 +692,8 @@ protected BoundExpression BindInferredVariableInitializer(DiagnosticBag diagnost // their own and therefore cannot be the initializer of an implicitly typed local. if (!expression.HasAnyErrors && !expression.HasExpressionType()) { - MessageID id = MessageID.IDS_NULL; - if (expression.Kind == BoundKind.UnboundLambda) - { - id = ((UnboundLambda)expression).MessageID; - } - else if (expression.Kind == BoundKind.MethodGroup) - { - id = MessageID.IDS_MethodGroup; - } - else - { - Debug.Assert(expression.IsLiteralNull(), "How did we successfully bind an expression without a type?"); - } - // Cannot assign {0} to an implicitly-typed local variable - Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, errorSyntax, id.Localize()); + Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, errorSyntax, expression.Display); } return expression; diff --git a/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs b/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs index f8d6d1affa101..95d323a4b7745 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Formatting.cs @@ -65,4 +65,12 @@ public override object Display get { throw ExceptionUtilities.Unreachable; } } } + + internal sealed partial class BoundThrowExpression + { + public override object Display + { + get { return MessageID.IDS_ThrowExpression.Localize(); } + } + } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 25e6bd5de17f2..f3c91ec86b23f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -10043,6 +10043,15 @@ internal static string IDS_Text { } } + /// + /// Looks up a localized string similar to <throw>. + /// + internal static string IDS_ThrowExpression { + get { + return ResourceManager.GetString("IDS_ThrowExpression", resourceCulture); + } + } + /// /// Looks up a localized string similar to Microsoft (R) Visual C# Compiler. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 832a7a363fdb1..33d4b005c2849 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -4164,6 +4164,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ byref locals and returns + + <throw> + Compilation (C#): diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 0690102891840..15293adb97335 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -119,6 +119,8 @@ internal enum MessageID IDS_FeatureLocalFunctions = MessageBase + 12708, IDS_FeatureRefLocalsReturns = MessageBase + 12710, + + IDS_ThrowExpression = MessageBase + 12711, } // Message IDs may refer to strings that need to be localized. diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs index c8d56cd86b836..1793eeada6aa2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs @@ -7785,5 +7785,26 @@ static bool Dummy(bool x, object y, object z) 1 2"); } + + [Fact] + public void ThrowName() + { + var source = +@" +class Program +{ + static void Main(string[] args) + { + var y = null ?? throw null; + } +} +"; + var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: patternParseOptions); + compilation.VerifyDiagnostics( + // (6,17): error CS0019: Operator '??' cannot be applied to operands of type '' and '' + // var y = null ?? throw null; + Diagnostic(ErrorCode.ERR_BadBinaryOps, "null ?? throw null").WithArguments("??", "", "").WithLocation(6, 17) + ); + } } }