diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationModifiers.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationModifiers.cs index 7f4f224ad24b3..87a30c1b2290d 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationModifiers.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationModifiers.cs @@ -36,6 +36,8 @@ internal enum DeclarationModifiers : uint Async = 1 << 20, Ref = 1 << 21, // used only for structs + Required = 1 << 22, // Used only for properties and fields + All = (1 << 23) - 1, // all modifiers Unset = 1 << 23, // used when a modifiers value hasn't yet been computed diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index d4869973012a7..f2f0381e2c0bf 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -238,6 +238,9 @@ internal enum MessageID IDS_FeatureGenericAttributes = MessageBase + 12812, + // PROTOTYPE(req): here for avoiding merge conflicts. Move to the end and condense before merge. + IDS_FeatureRequiredMembers = MessageBase + 13000, + IDS_FeatureNewLinesInInterpolations = MessageBase + 12813, IDS_FeatureListPattern = MessageBase + 12814, IDS_ParameterNullChecking = MessageBase + 12815, @@ -354,6 +357,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureGenericAttributes: // semantic check case MessageID.IDS_FeatureNewLinesInInterpolations: // semantic check case MessageID.IDS_FeatureListPattern: // semantic check + case MessageID.IDS_FeatureRequiredMembers: // semantic check case MessageID.IDS_ParameterNullChecking: // syntax check return LanguageVersion.Preview; diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 7f8ef199e869d..aa11549bb37de 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -109,6 +109,7 @@ modifier | 'public' | 'readonly' | 'ref' + | 'required' | 'sealed' | 'static' | 'unsafe' diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 32227d5860afa..7bea98e33b01f 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -1162,6 +1162,8 @@ internal static DeclarationModifiers GetModifier(SyntaxKind kind, SyntaxKind con return DeclarationModifiers.Partial; case SyntaxKind.AsyncKeyword: return DeclarationModifiers.Async; + case SyntaxKind.RequiredKeyword: + return DeclarationModifiers.Required; } goto default; @@ -1243,7 +1245,7 @@ private void ParseModifiers(SyntaxListBuilder tokens, bool forAccessors) } case DeclarationModifiers.Async: - if (!ShouldAsyncBeTreatedAsModifier(parsingStatementNotDeclaration: false)) + if (!ShouldAsyncOrRequiredBeTreatedAsModifier(parsingStatementNotDeclaration: false)) { return; } @@ -1252,6 +1254,19 @@ private void ParseModifiers(SyntaxListBuilder tokens, bool forAccessors) modTok = CheckFeatureAvailability(modTok, MessageID.IDS_FeatureAsync); break; + case DeclarationModifiers.Required: + // In C# 11, required in a modifier position is always a keyword if not escaped. Otherwise, we reuse the async detection + // machinery to make a conservative guess as to whether the user meant required to be a keyword, so that they get a good langver + // diagnostic and all the machinery to upgrade their project kicks in. + if (!IsFeatureEnabled(MessageID.IDS_FeatureRequiredMembers) && !ShouldAsyncOrRequiredBeTreatedAsModifier(parsingStatementNotDeclaration: false)) + { + return; + } + + modTok = ConvertToKeyword(this.EatToken()); + + break; + default: modTok = this.EatToken(); break; @@ -1280,16 +1295,16 @@ bool isStructOrRecordKeyword(SyntaxToken token) } } - private bool ShouldAsyncBeTreatedAsModifier(bool parsingStatementNotDeclaration) + private bool ShouldAsyncOrRequiredBeTreatedAsModifier(bool parsingStatementNotDeclaration) { - Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.AsyncKeyword); + Debug.Assert(this.CurrentToken.ContextualKind is SyntaxKind.AsyncKeyword or SyntaxKind.RequiredKeyword); // Adapted from CParser::IsAsyncMethod. if (IsNonContextualModifier(PeekToken(1))) { // If the next token is a (non-contextual) modifier keyword, then this token is - // definitely the async keyword + // definitely the async or required keyword return true; } @@ -1300,7 +1315,7 @@ private bool ShouldAsyncBeTreatedAsModifier(bool parsingStatementNotDeclaration) try { - this.EatToken(); //move past contextual 'async' + this.EatToken(); //move past contextual 'async' or 'required' if (!parsingStatementNotDeclaration && (this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword)) @@ -1317,6 +1332,8 @@ private bool ShouldAsyncBeTreatedAsModifier(bool parsingStatementNotDeclaration) // ... 'async' [partial] ... // DEVNOTE: Although we parse async user defined conversions, operators, etc. here, // anything other than async methods are detected as erroneous later, during the define phase + // The comments in general were not updated to add "async or required" everywhere to preserve + // history, but generally wherever async occurs, it can also be required. if (!parsingStatementNotDeclaration) { @@ -2627,6 +2644,7 @@ private bool IsMisplacedModifier(SyntaxListBuilder modifiers, SyntaxList Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.LineDirectivePosition = 9070 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.LineSpanDirectiveTrivia = 9071 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.RequiredKeyword = 8447 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Externs.get -> Microsoft.CodeAnalysis.SyntaxList *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Members.get -> Microsoft.CodeAnalysis.SyntaxList *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Name.get -> Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index ecff495816ec2..ad1ac640a2e05 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -396,6 +396,8 @@ public enum SyntaxKind : ushort ManagedKeyword = 8445, /// Represents . UnmanagedKeyword = 8446, + /// Represents . + RequiredKeyword = 8447, // when adding a contextual keyword following functions must be adapted: // diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index 2b39e146d4f58..a5edff8780b64 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs @@ -1075,7 +1075,7 @@ public static SyntaxKind GetPreprocessorKeywordKind(string text) public static IEnumerable GetContextualKeywordKinds() { - for (int i = (int)SyntaxKind.YieldKeyword; i <= (int)SyntaxKind.UnmanagedKeyword; i++) + for (int i = (int)SyntaxKind.YieldKeyword; i <= (int)SyntaxKind.RequiredKeyword; i++) { yield return (SyntaxKind)i; } @@ -1128,6 +1128,7 @@ public static bool IsContextualKeyword(SyntaxKind kind) case SyntaxKind.RecordKeyword: case SyntaxKind.ManagedKeyword: case SyntaxKind.UnmanagedKeyword: + case SyntaxKind.RequiredKeyword: return true; default: return false; @@ -1247,6 +1248,8 @@ public static SyntaxKind GetContextualKeywordKind(string text) return SyntaxKind.ManagedKeyword; case "unmanaged": return SyntaxKind.UnmanagedKeyword; + case "required": + return SyntaxKind.RequiredKeyword; default: return SyntaxKind.None; } @@ -1684,6 +1687,8 @@ public static string GetText(SyntaxKind kind) return "managed"; case SyntaxKind.UnmanagedKeyword: return "unmanaged"; + case SyntaxKind.RequiredKeyword: + return "required"; default: return string.Empty; } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs index e6182b885b207..df9c75d86e24d 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/MemberDeclarationParsingTests.cs @@ -4,6 +4,7 @@ #nullable disable +using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -34,6 +35,10 @@ private SyntaxTree UsingTree(string text, CSharpParseOptions options, params Dia return tree; } + private static readonly CSharpParseOptions RequiredMembersOptions = TestOptions.RegularNext; + public static readonly IEnumerable Regular10AndScriptAndRequiredMembersMinimum = new[] { new[] { TestOptions.Regular10 }, new[] { RequiredMembersOptions }, new[] { TestOptions.Script.WithLanguageVersion(LanguageVersion.CSharp10) } }; + public static readonly IEnumerable Regular10AndScript = new[] { new[] { TestOptions.Regular10 }, new[] { TestOptions.Script.WithLanguageVersion(LanguageVersion.CSharp10) } }; + [Fact] [WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")] public void ParsePrivate() @@ -906,6 +911,608 @@ public void SetAndInitAccessor() } } + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScriptAndRequiredMembersMinimum))] + public void RequiredModifierProperty_01(CSharpParseOptions parseOptions) + { + UsingDeclaration("required string Prop { get; }", options: parseOptions); + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Prop"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScript))] + public void RequiredModifierProperty_02(CSharpParseOptions parseOptions) + { + UsingDeclaration("required Prop { get; }", options: parseOptions); + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "required"); + } + N(SyntaxKind.IdentifierToken, "Prop"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.RequiredMembers)] + public void RequiredModifierProperty_03() + { + UsingDeclaration("required Prop { get; }", options: RequiredMembersOptions, + // (1,1): error CS1073: Unexpected token '{' + // required Prop { get; } + Diagnostic(ErrorCode.ERR_UnexpectedToken, "required Prop").WithArguments("{").WithLocation(1, 1), + // (1,15): error CS1519: Invalid token '{' in class, record, struct, or interface member declaration + // required Prop { get; } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(1, 15) + ); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScript))] + public void RequiredModifierProperty_04(CSharpParseOptions parseOptions) + { + UsingDeclaration("required required { get; }", options: parseOptions); + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "required"); + } + N(SyntaxKind.IdentifierToken, "required"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.RequiredMembers)] + public void RequiredModifierProperty_05() + { + UsingDeclaration("required required { get; }", options: RequiredMembersOptions, + // (1,1): error CS1073: Unexpected token '{' + // required required { get; } + Diagnostic(ErrorCode.ERR_UnexpectedToken, "required required").WithArguments("{").WithLocation(1, 1), + // (1,19): error CS1519: Invalid token '{' in class, record, struct, or interface member declaration + // required required { get; } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(1, 19) + ); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.RequiredKeyword); + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScriptAndRequiredMembersMinimum))] + public void RequiredModifierField_01(CSharpParseOptions parseOptions) + { + UsingDeclaration("required string Field;", options: parseOptions); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "Field"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScript))] + public void RequiredModifierField_02(CSharpParseOptions parseOptions) + { + UsingDeclaration("required Field;", options: parseOptions); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "required"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "Field"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.RequiredMembers)] + public void RequiredModifierField_03() + { + UsingDeclaration("required Field;", options: RequiredMembersOptions, + // (1,1): error CS1073: Unexpected token ';' + // required Field; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "required Field").WithArguments(";").WithLocation(1, 1), + // (1,15): error CS1519: Invalid token ';' in class, record, struct, or interface member declaration + // required Field; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(1, 15) + ); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Field"); + } + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScript))] + public void RequiredModifierField_04(CSharpParseOptions parseOptions) + { + UsingDeclaration("required required;", options: parseOptions); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "required"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "required"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.RequiredMembers)] + public void RequiredModifierField_05() + { + UsingDeclaration("required required;", options: RequiredMembersOptions, + // (1,1): error CS1073: Unexpected token ';' + // required required; + Diagnostic(ErrorCode.ERR_UnexpectedToken, "required required").WithArguments(";").WithLocation(1, 1), + // (1,18): error CS1519: Invalid token ';' in class, record, struct, or interface member declaration + // required required; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(1, 18) + ); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.RequiredKeyword); + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScriptAndRequiredMembersMinimum))] + public void RequiredModifierMethod_01(CSharpParseOptions parseOptions) + { + // Note this is a semantic error, not a syntactic one + UsingDeclaration("required string M() {}", options: parseOptions); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScript))] + public void RequiredModifierMethod_02(CSharpParseOptions parseOptions) + { + UsingDeclaration("required M() {}", options: parseOptions); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "required"); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.RequiredMembers)] + public void RequiredModifierMethod_03() + { + UsingDeclaration("required M() {}", options: RequiredMembersOptions); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScriptAndRequiredMembersMinimum))] + public void RequiredModifierOperator(CSharpParseOptions parseOptions) + { + // Note this is a semantic error, not a syntactic one + UsingDeclaration("static required C operator+(C c1, C c2) {}", options: parseOptions); + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "C"); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "C"); + } + N(SyntaxKind.IdentifierToken, "c1"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "C"); + } + N(SyntaxKind.IdentifierToken, "c2"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScriptAndRequiredMembersMinimum))] + public void RequiredModifierConversion_01(CSharpParseOptions parseOptions) + { + // Note this is a semantic error, not a syntactic one + UsingDeclaration("static required implicit operator C(S s) {}", options: parseOptions); + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "C"); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + N(SyntaxKind.IdentifierToken, "s"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScript))] + public void RequiredModifierConversion_02(CSharpParseOptions parseOptions) + { + UsingDeclaration("static implicit required operator C(S s) {}", options: parseOptions, + // (1,17): error CS8652: The feature 'static abstract members in interfaces' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static implicit required operator C(S s) {} + Diagnostic(ErrorCode.ERR_FeatureInPreview, "required ").WithArguments("static abstract members in interfaces").WithLocation(1, 17), + // (1,26): error CS1003: Syntax error, '.' expected + // static implicit required operator C(S s) {} + Diagnostic(ErrorCode.ERR_SyntaxError, "operator").WithArguments(".", "operator").WithLocation(1, 26) + ); + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.ExplicitInterfaceSpecifier); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "required"); + } + M(SyntaxKind.DotToken); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "C"); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "S"); + } + N(SyntaxKind.IdentifierToken, "s"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScriptAndRequiredMembersMinimum))] + public void RequiredModifierIncompleteProperty_01(CSharpParseOptions parseOptions) + { + UsingDeclaration("required string Prop { get;", options: parseOptions, + // (1,28): error CS1513: } expected + // required string Prop { get; + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(1, 28) + ); + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Prop"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.SemicolonToken); + } + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScriptAndRequiredMembersMinimum))] + public void RequiredModifierIncompleteProperty_02(CSharpParseOptions parseOptions) + { + UsingDeclaration("required string Prop {", options: parseOptions, + // (1,23): error CS1513: } expected + // required string Prop { + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(1, 23) + ); + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "Prop"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScriptAndRequiredMembersMinimum))] + public void RequiredModifierIncompleteMember_01(CSharpParseOptions parseOptions) + { + UsingDeclaration("required string Prop", options: parseOptions, + // (1,21): error CS1002: ; expected + // required string Prop + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 21) + ); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "Prop"); + } + } + M(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScriptAndRequiredMembersMinimum))] + public void RequiredModifierIncompleteMember_02(CSharpParseOptions parseOptions) + { + UsingDeclaration("required string", options: parseOptions, + // (1,16): error CS1519: Invalid token '' in class, record, struct, or interface member declaration + // required string + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "").WithArguments("").WithLocation(1, 16) + ); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScriptAndRequiredMembersMinimum))] + public void RequiredModifierIncompleteMember_03(CSharpParseOptions parseOptions) + { + UsingDeclaration("required C", options: parseOptions, + // (1,11): error CS1519: Invalid token '' in class, record, struct, or interface member declaration + // required C + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "").WithArguments("").WithLocation(1, 11) + ); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.RequiredKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "C"); + } + } + EOF(); + } + + [Theory, CompilerTrait(CompilerFeature.RequiredMembers)] + [MemberData(nameof(Regular10AndScript))] + public void RequiredModifierIncompleteMember_04(CSharpParseOptions parseOptions) + { + UsingDeclaration("required", options: parseOptions, + // (1,9): error CS1519: Invalid token '' in class, record, struct, or interface member declaration + // required + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "").WithArguments("").WithLocation(1, 9) + ); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "required"); + } + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.RequiredMembers)] + public void RequiredModifierIncompleteMember_05() + { + UsingDeclaration("required", options: RequiredMembersOptions, + // (1,9): error CS1519: Invalid token '' in class, record, struct, or interface member declaration + // required + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "").WithArguments("").WithLocation(1, 9) + ); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.RequiredKeyword); + } + EOF(); + } + [Fact] public void OperatorDeclaration_ExplicitImplementation_01() { diff --git a/src/Compilers/Test/Core/Traits/CompilerFeature.cs b/src/Compilers/Test/Core/Traits/CompilerFeature.cs index 7a58820fda4fb..a5503dc6d8e7f 100644 --- a/src/Compilers/Test/Core/Traits/CompilerFeature.cs +++ b/src/Compilers/Test/Core/Traits/CompilerFeature.cs @@ -43,5 +43,6 @@ public enum CompilerFeature ModuleInitializers, FunctionPointers, RecordStructs, + RequiredMembers, } }