未验证 提交 6752e31c 编写于 作者: R Rikki Gibson 提交者: GitHub

Readonly members syntax and symbol API (#32888)

* Add syntax tests for readonly members

* Add simple test for readonly struct method

* Allow readonly modifier on methods in structs

* Add simple readonly class method test

* Add SourceMemberMethodSymbol.IsReadOnly

* Test readonly static method

* Disallow readonly accessors on static properties

* Check usages of readonly keyword in property (not accessor) declarations

* Add ref-returning readonly method test

* Add readonly ref readonly parsing test

* Allow readonly modifier on property decls in general. Add more tests.

* Add indexer tests

* Readonly events on structs

* Remove comment

* Disallow readonly on events with associated fields

* Test static readonly auto properties and destructors

* Disallow redundant readonly on accessor. Simplify diagnostics for static readonly property.

* Add tests based on feedback

* Note that feature checks and corresponding tests are needed
上级 6b4b64aa
......@@ -368,6 +368,11 @@ public sealed override bool IsVirtual
get { return (_modifiers & DeclarationModifiers.Virtual) != 0; }
}
internal bool IsReadOnly
{
get { return (_modifiers & DeclarationModifiers.ReadOnly) != 0; }
}
public sealed override Accessibility DeclaredAccessibility
{
get { return ModifierUtils.EffectiveAccessibility(_modifiers); }
......@@ -435,6 +440,11 @@ private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, bool expli
}
}
if (this.ContainingType.IsStructType())
{
allowedModifiers |= DeclarationModifiers.ReadOnly;
}
if (!isInterface)
{
allowedModifiers |= DeclarationModifiers.Extern;
......@@ -468,6 +478,11 @@ protected void CheckModifiersAndType(DiagnosticBag diagnostics)
// A static member '{0}' cannot be marked as override, virtual, or abstract
diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, location, this);
}
else if (IsReadOnly && (IsStatic || HasAssociatedField))
{
// The modifier '{0}' is not valid for this item
diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.ReadOnlyKeyword));
}
else if (IsOverride && (IsNew || IsVirtual))
{
// A member '{0}' marked as override cannot be marked as new or virtual
......
......@@ -509,6 +509,14 @@ public sealed override bool IsAsync
}
}
internal bool IsReadOnly
{
get
{
return (this.DeclarationModifiers & DeclarationModifiers.ReadOnly) != 0;
}
}
internal sealed override Cci.CallingConvention CallingConvention
{
get
......
......@@ -772,6 +772,11 @@ private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, MethodKind
allowedModifiers |= DeclarationModifiers.Extern | DeclarationModifiers.Async;
}
if (ContainingType.IsStructType())
{
allowedModifiers |= DeclarationModifiers.ReadOnly;
}
var mods = ModifierUtils.MakeAndCheckNontypeMemberModifiers(modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors);
this.CheckUnsafeModifier(mods, diagnostics);
......@@ -933,6 +938,11 @@ private void CheckModifiers(bool hasBody, Location location, DiagnosticBag diagn
// The modifier '{0}' is not valid for this item
diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.VirtualKeyword));
}
else if (IsStatic && IsReadOnly)
{
// The modifier '{0}' is not valid for this item
diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.ReadOnlyKeyword));
}
else if (IsAbstract && !ContainingType.IsAbstract && (ContainingType.TypeKind == TypeKind.Class || ContainingType.TypeKind == TypeKind.Submission))
{
// '{0}' is abstract but it is contained in non-abstract class '{1}'
......
......@@ -410,7 +410,12 @@ private DeclarationModifiers MakeModifiers(AccessorDeclarationSyntax syntax, Loc
const DeclarationModifiers defaultAccess = DeclarationModifiers.None;
// Check that the set of modifiers is allowed
const DeclarationModifiers allowedModifiers = DeclarationModifiers.AccessibilityMask;
DeclarationModifiers allowedModifiers = DeclarationModifiers.AccessibilityMask;
if (this.ContainingType.IsStructType() && !_property.HasReadOnlyModifier)
{
allowedModifiers |= DeclarationModifiers.ReadOnly;
}
var mods = ModifierUtils.MakeAndCheckNontypeMemberModifiers(syntax.Modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors);
// For interface, check there are no accessibility modifiers.
......@@ -451,6 +456,11 @@ private void CheckModifiers(Location location, bool hasBody, bool isAutoProperty
{
diagnostics.Add(AccessCheck.GetProtectedMemberInSealedTypeError(ContainingType), location, this);
}
else if (IsStatic && IsReadOnly && !_property.HasReadOnlyModifier)
{
// The modifier '{0}' is not valid for this item
diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.ReadOnlyKeyword));
}
}
/// <summary>
......
......@@ -627,6 +627,8 @@ internal bool IsNew
get { return (_modifiers & DeclarationModifiers.New) != 0; }
}
internal bool HasReadOnlyModifier => (_modifiers & DeclarationModifiers.ReadOnly) != 0;
public override MethodSymbol GetMethod
{
get { return _getMethod; }
......@@ -813,6 +815,11 @@ private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, bool isExp
}
}
if (ContainingType.IsStructType())
{
allowedModifiers |= DeclarationModifiers.ReadOnly;
}
if (!isInterface)
{
allowedModifiers |=
......@@ -890,6 +897,11 @@ private void CheckModifiers(Location location, bool isIndexer, DiagnosticBag dia
// A static member '{0}' cannot be marked as override, virtual, or abstract
diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, location, this);
}
else if (IsStatic && HasReadOnlyModifier)
{
// The modifier '{0}' is not valid for this item
diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.ReadOnlyKeyword));
}
else if (IsOverride && (IsNew || IsVirtual))
{
// A member '{0}' marked as override cannot be marked as new or virtual
......
......@@ -249,5 +249,507 @@ public static void Main()
Diagnostic(ErrorCode.ERR_AssgReadonlyStatic2, "s.field").WithArguments("Program.s").WithLocation(8, 9)
);
}
// PROTOTYPE: readonly members features should require C# 8.0 or greater
[Fact]
public void ReadOnlyStructMethod()
{
var csharp = @"
public struct S
{
public int i;
public readonly int M()
{
return i;
}
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics();
var method = (SourceMemberMethodSymbol)comp.GetMember<NamedTypeSymbol>("S").GetMember<MethodSymbol>("M");
// PROTOTYPE: add `public abstract bool IsReadOnly` to MethodSymbol and implement in subtypes?
Assert.True(method.IsReadOnly);
}
[Fact]
public void ReadOnlyClass()
{
var csharp = @"
using System;
public readonly class C
{
public readonly int M() => 42;
public readonly int P { get; set; }
public readonly int this[int i] => i;
public readonly event Action<EventArgs> E { add {} remove {} }
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (4,23): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly class C
Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("readonly").WithLocation(4, 23),
// (6,25): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly int M() => 42;
Diagnostic(ErrorCode.ERR_BadMemberFlag, "M").WithArguments("readonly").WithLocation(6, 25),
// (7,25): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly int P { get; set; }
Diagnostic(ErrorCode.ERR_BadMemberFlag, "P").WithArguments("readonly").WithLocation(7, 25),
// (8,25): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly int this[int i] => i;
Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("readonly").WithLocation(8, 25),
// (9,45): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly event Action<EventArgs> E { add {} remove {} }
Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("readonly").WithLocation(9, 45));
}
[Fact]
public void ReadOnlyInterface()
{
var csharp = @"
using System;
public readonly interface I
{
readonly int M();
readonly int P { get; set; }
readonly int this[int i] { get; }
readonly event Action<EventArgs> E;
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (4,27): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly interface I
Diagnostic(ErrorCode.ERR_BadMemberFlag, "I").WithArguments("readonly").WithLocation(4, 27),
// (6,18): error CS0106: The modifier 'readonly' is not valid for this item
// readonly int M();
Diagnostic(ErrorCode.ERR_BadMemberFlag, "M").WithArguments("readonly").WithLocation(6, 18),
// (7,18): error CS0106: The modifier 'readonly' is not valid for this item
// readonly int P { get; set; }
Diagnostic(ErrorCode.ERR_BadMemberFlag, "P").WithArguments("readonly").WithLocation(7, 18),
// (8,18): error CS0106: The modifier 'readonly' is not valid for this item
// readonly int this[int i] { get; }
Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("readonly").WithLocation(8, 18),
// (9,38): error CS0106: The modifier 'readonly' is not valid for this item
// readonly event Action<EventArgs> E;
Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("readonly").WithLocation(9, 38));
}
[Fact]
public void ReadOnlyEnum()
{
var csharp = @"
public readonly enum E
{
readonly A, readonly B
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (2,22): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly enum E
Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("readonly").WithLocation(2, 22),
// (3,2): error CS1041: Identifier expected; 'readonly' is a keyword
// {
Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "").WithArguments("", "readonly").WithLocation(3, 2),
// (4,17): error CS1041: Identifier expected; 'readonly' is a keyword
// readonly A, readonly B;
Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "readonly").WithArguments("", "readonly").WithLocation(4, 17));
}
[Fact]
public void ReadOnlyStructStaticMethod()
{
var csharp = @"
public struct S
{
public static int i;
public static readonly int M()
{
return i;
}
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (5,32): error CS0106: The modifier 'readonly' is not valid for this item
// public static readonly int M()
Diagnostic(ErrorCode.ERR_BadMemberFlag, "M").WithArguments("readonly").WithLocation(5, 32));
}
[Fact]
public void ReadOnlyStructProperty()
{
var csharp = @"
public struct S
{
public int i;
public int P
{
readonly get
{
return i;
}
}
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics();
}
[Fact]
public void ReadOnlyStructStaticProperty()
{
var csharp = @"
public struct S
{
public static int i;
public static int P
{
readonly get
{
return i;
}
}
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (7,18): error CS0106: The modifier 'readonly' is not valid for this item
// readonly get
Diagnostic(ErrorCode.ERR_BadMemberFlag, "get").WithArguments("readonly").WithLocation(7, 18));
}
[Fact]
public void ReadOnlyStructStaticExpressionProperty()
{
var csharp = @"
public struct S
{
public static int i;
public static readonly int P => i;
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (5,32): error CS0106: The modifier 'readonly' is not valid for this item
// public static readonly int P => i;
Diagnostic(ErrorCode.ERR_BadMemberFlag, "P").WithArguments("readonly").WithLocation(5, 32));
}
[Fact]
public void ReadOnlyStructExpressionProperty()
{
var csharp = @"
public struct S
{
public int i;
public readonly int P => i;
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics();
}
[Fact]
public void ReadOnlyStructBlockProperty()
{
var csharp = @"
public struct S
{
public int i;
public readonly int P { get { return i; } }
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics();
}
[Fact]
public void ReadOnlyAutoProperty()
{
var csharp = @"
public struct S
{
public int P1 { readonly get; }
public readonly int P2 { get; }
public int P3 { readonly get; set; }
public int P4 { readonly get; readonly set; } // PROTOTYPE: readonly set on an auto-property should give an error
public readonly int P5 { get; set; } // PROTOTYPE: readonly set on an auto-property should give an error
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics();
}
[Fact]
public void ReadOnlyProperty_RedundantReadOnlyAccessor()
{
var csharp = @"
public struct S
{
public readonly int P { readonly get; }
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (4,38): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly int P { readonly get; }
Diagnostic(ErrorCode.ERR_BadMemberFlag, "get").WithArguments("readonly").WithLocation(4, 38));
}
[Fact]
public void ReadOnlyStaticAutoProperty()
{
var csharp = @"
public struct S
{
public static readonly int P1 { get; set; }
public static int P2 { readonly get; }
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (4,32): error CS0106: The modifier 'readonly' is not valid for this item
// public static readonly int P1 { get; set; }
Diagnostic(ErrorCode.ERR_BadMemberFlag, "P1").WithArguments("readonly").WithLocation(4, 32),
// (5,37): error CS0106: The modifier 'readonly' is not valid for this item
// public static int P2 { readonly get; }
Diagnostic(ErrorCode.ERR_BadMemberFlag, "get").WithArguments("readonly").WithLocation(5, 37));
}
[Fact]
public void RefReturningReadOnlyMethod()
{
// PROTOTYPE: would be good to add some more mutation here
// as well as expected diagnostics once that part of the feature is ready.
var csharp = @"
public struct S
{
private static int f1;
public readonly ref int M1() => ref f1;
private static readonly int f2;
public readonly ref readonly int M2() => ref f2;
private static readonly int f3;
public ref readonly int M3()
{
f1++;
return ref f3;
}
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics();
}
[Fact]
public void ReadOnlyConstructor()
{
var csharp = @"
public struct S
{
static readonly S() { }
public readonly S(int i) { }
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (4,21): error CS0106: The modifier 'readonly' is not valid for this item
// static readonly S() { }
Diagnostic(ErrorCode.ERR_BadMemberFlag, "S").WithArguments("readonly").WithLocation(4, 21),
// (5,21): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly S(int i) { }
Diagnostic(ErrorCode.ERR_BadMemberFlag, "S").WithArguments("readonly").WithLocation(5, 21));
}
[Fact]
public void ReadOnlyDestructor()
{
var csharp = @"
public struct S
{
readonly ~S() { }
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (4,15): error CS0106: The modifier 'readonly' is not valid for this item
// readonly ~S() { }
Diagnostic(ErrorCode.ERR_BadMemberFlag, "S").WithArguments("readonly").WithLocation(4, 15),
// (4,15): error CS0575: Only class types can contain destructors
// readonly ~S() { }
Diagnostic(ErrorCode.ERR_OnlyClassesCanContainDestructors, "S").WithArguments("S.~S()").WithLocation(4, 15));
}
[Fact]
public void ReadOnlyOperator()
{
var csharp = @"
public struct S
{
public static readonly S operator +(S lhs, S rhs) => lhs;
public static readonly explicit operator int(S s) => 42;
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (4,39): error CS0106: The modifier 'readonly' is not valid for this item
// public static readonly S operator +(S lhs, S rhs) => lhs;
Diagnostic(ErrorCode.ERR_BadMemberFlag, "+").WithArguments("readonly").WithLocation(4, 39),
// (5,46): error CS0106: The modifier 'readonly' is not valid for this item
// public static readonly explicit operator int(S s) => 42;
Diagnostic(ErrorCode.ERR_BadMemberFlag, "int").WithArguments("readonly").WithLocation(5, 46));
}
[Fact]
public void ReadOnlyDelegate()
{
var csharp = @"
public readonly delegate int Del();
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (2,30): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly delegate int Del();
Diagnostic(ErrorCode.ERR_BadMemberFlag, "Del").WithArguments("readonly").WithLocation(2, 30));
}
[Fact]
public void ReadOnlyIndexer()
{
var csharp = @"
public struct S1
{
public readonly int this[int i]
{
get => 42;
set {}
}
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics();
}
[Fact]
public void ReadOnlyExpressionIndexer()
{
var csharp = @"
public struct S1
{
public readonly int this[int i] => 42;
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics();
}
[Fact]
public void ReadOnlyGetExpressionIndexer()
{
var csharp = @"
public struct S1
{
public int this[int i]
{
readonly get => 42;
readonly set {}
}
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics();
}
[Fact]
public void ReadOnlyFieldLikeEvent()
{
var csharp = @"
using System;
public struct S1
{
public readonly event Action<EventArgs> E;
public void M() { E?.Invoke(new EventArgs()); }
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (6,45): error CS0106: The modifier 'readonly' is not valid for this item
// public readonly event Action<EventArgs> E;
Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("readonly").WithLocation(6, 45));
}
[Fact]
public void ReadOnlyEventExplicitAddRemove()
{
var csharp = @"
using System;
public struct S1
{
public readonly event Action<EventArgs> E
{
add {}
remove {}
}
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics();
}
[Fact]
public void ReadOnlyStaticEvent()
{
var csharp = @"
using System;
public struct S1
{
public static readonly event Action<EventArgs> E
{
add {}
remove {}
}
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (6,52): error CS0106: The modifier 'readonly' is not valid for this item
// public static readonly event Action<EventArgs> E
Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("readonly").WithLocation(6, 52));
}
[Fact]
public void ReadOnlyEventReadOnlyAccessors()
{
var csharp = @"
using System;
public struct S1
{
public event Action<EventArgs> E
{
readonly add {}
readonly remove {}
}
}
";
var comp = CreateCompilation(csharp);
comp.VerifyDiagnostics(
// (8,9): error CS1609: Modifiers cannot be placed on event accessor declarations
// readonly add {}
Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "readonly").WithLocation(8, 9),
// (9,9): error CS1609: Modifiers cannot be placed on event accessor declarations
// readonly remove {}
Diagnostic(ErrorCode.ERR_NoModifiersOnAccessor, "readonly").WithLocation(9, 9));
}
}
}
......@@ -2729,6 +2729,184 @@ public void TestClassMethodWithPartial()
Assert.Equal(SyntaxKind.None, ms.SemicolonToken.Kind());
}
[Fact]
public void TestStructMethodWithReadonly()
{
var text = "struct a { readonly void M() { } }";
var file = this.ParseFile(text);
Assert.NotNull(file);
Assert.Equal(1, file.Members.Count);
Assert.Equal(text, file.ToString());
Assert.Equal(0, file.Errors().Length);
Assert.Equal(SyntaxKind.StructDeclaration, file.Members[0].Kind());
var structDecl = (TypeDeclarationSyntax)file.Members[0];
Assert.Equal(0, structDecl.AttributeLists.Count);
Assert.Equal(0, structDecl.Modifiers.Count);
Assert.NotNull(structDecl.Keyword);
Assert.Equal(SyntaxKind.StructKeyword, structDecl.Keyword.Kind());
Assert.NotNull(structDecl.Identifier);
Assert.Equal("a", structDecl.Identifier.ToString());
Assert.Null(structDecl.BaseList);
Assert.Equal(0, structDecl.ConstraintClauses.Count);
Assert.NotNull(structDecl.OpenBraceToken);
Assert.NotNull(structDecl.CloseBraceToken);
Assert.Equal(1, structDecl.Members.Count);
Assert.Equal(SyntaxKind.MethodDeclaration, structDecl.Members[0].Kind());
var ms = (MethodDeclarationSyntax)structDecl.Members[0];
Assert.Equal(0, ms.AttributeLists.Count);
Assert.Equal(1, ms.Modifiers.Count);
Assert.Equal(SyntaxKind.ReadOnlyKeyword, ms.Modifiers[0].Kind());
Assert.NotNull(ms.ReturnType);
Assert.Equal("void", ms.ReturnType.ToString());
Assert.NotNull(ms.Identifier);
Assert.Equal("M", ms.Identifier.ToString());
Assert.NotNull(ms.ParameterList.OpenParenToken);
Assert.False(ms.ParameterList.OpenParenToken.IsMissing);
Assert.Equal(0, ms.ParameterList.Parameters.Count);
Assert.NotNull(ms.ParameterList.CloseParenToken);
Assert.False(ms.ParameterList.CloseParenToken.IsMissing);
Assert.Equal(0, ms.ConstraintClauses.Count);
Assert.NotNull(ms.Body);
Assert.NotEqual(SyntaxKind.None, ms.Body.OpenBraceToken.Kind());
Assert.NotEqual(SyntaxKind.None, ms.Body.CloseBraceToken.Kind());
Assert.Equal(SyntaxKind.None, ms.SemicolonToken.Kind());
}
[Fact]
public void TestReadOnlyRefReturning()
{
var text = "struct a { readonly ref readonly int M() { } }";
var file = this.ParseFile(text);
Assert.NotNull(file);
Assert.Equal(1, file.Members.Count);
Assert.Equal(text, file.ToString());
Assert.Equal(0, file.Errors().Length);
Assert.Equal(SyntaxKind.StructDeclaration, file.Members[0].Kind());
var structDecl = (TypeDeclarationSyntax)file.Members[0];
Assert.Equal(0, structDecl.AttributeLists.Count);
Assert.Equal(0, structDecl.Modifiers.Count);
Assert.NotNull(structDecl.Keyword);
Assert.Equal(SyntaxKind.StructKeyword, structDecl.Keyword.Kind());
Assert.NotNull(structDecl.Identifier);
Assert.Equal("a", structDecl.Identifier.ToString());
Assert.Null(structDecl.BaseList);
Assert.Equal(0, structDecl.ConstraintClauses.Count);
Assert.NotNull(structDecl.OpenBraceToken);
Assert.NotNull(structDecl.CloseBraceToken);
Assert.Equal(1, structDecl.Members.Count);
Assert.Equal(SyntaxKind.MethodDeclaration, structDecl.Members[0].Kind());
var ms = (MethodDeclarationSyntax)structDecl.Members[0];
Assert.Equal(0, ms.AttributeLists.Count);
Assert.Equal(1, ms.Modifiers.Count);
Assert.Equal(SyntaxKind.ReadOnlyKeyword, ms.Modifiers[0].Kind());
Assert.Equal(SyntaxKind.RefType, ms.ReturnType.Kind());
var rt = (RefTypeSyntax)ms.ReturnType;
Assert.Equal(SyntaxKind.RefKeyword, rt.RefKeyword.Kind());
Assert.Equal(SyntaxKind.ReadOnlyKeyword, rt.ReadOnlyKeyword.Kind());
Assert.Equal("int", rt.Type.ToString());
Assert.NotNull(ms.Identifier);
Assert.Equal("M", ms.Identifier.ToString());
Assert.NotNull(ms.ParameterList.OpenParenToken);
Assert.False(ms.ParameterList.OpenParenToken.IsMissing);
Assert.Equal(0, ms.ParameterList.Parameters.Count);
Assert.NotNull(ms.ParameterList.CloseParenToken);
Assert.False(ms.ParameterList.CloseParenToken.IsMissing);
Assert.Equal(0, ms.ConstraintClauses.Count);
Assert.NotNull(ms.Body);
Assert.NotEqual(SyntaxKind.None, ms.Body.OpenBraceToken.Kind());
Assert.NotEqual(SyntaxKind.None, ms.Body.CloseBraceToken.Kind());
Assert.Equal(SyntaxKind.None, ms.SemicolonToken.Kind());
}
[Fact]
public void TestStructExpressionPropertyWithReadonly()
{
var text = "struct a { readonly int M => 42; }";
var file = this.ParseFile(text);
Assert.NotNull(file);
Assert.Equal(1, file.Members.Count);
Assert.Equal(text, file.ToString());
Assert.Equal(0, file.Errors().Length);
Assert.Equal(SyntaxKind.StructDeclaration, file.Members[0].Kind());
var structDecl = (TypeDeclarationSyntax)file.Members[0];
Assert.Equal(0, structDecl.AttributeLists.Count);
Assert.Equal(0, structDecl.Modifiers.Count);
Assert.NotNull(structDecl.Keyword);
Assert.Equal(SyntaxKind.StructKeyword, structDecl.Keyword.Kind());
Assert.NotNull(structDecl.Identifier);
Assert.Equal("a", structDecl.Identifier.ToString());
Assert.Null(structDecl.BaseList);
Assert.Equal(0, structDecl.ConstraintClauses.Count);
Assert.NotNull(structDecl.OpenBraceToken);
Assert.NotNull(structDecl.CloseBraceToken);
Assert.Equal(1, structDecl.Members.Count);
Assert.Equal(SyntaxKind.PropertyDeclaration, structDecl.Members[0].Kind());
var propertySyntax = (PropertyDeclarationSyntax)structDecl.Members[0];
Assert.Equal(0, propertySyntax.AttributeLists.Count);
Assert.Equal(1, propertySyntax.Modifiers.Count);
Assert.Equal(SyntaxKind.ReadOnlyKeyword, propertySyntax.Modifiers[0].Kind());
Assert.NotNull(propertySyntax.Type);
Assert.Equal("int", propertySyntax.Type.ToString());
Assert.NotNull(propertySyntax.Identifier);
Assert.Equal("M", propertySyntax.Identifier.ToString());
Assert.NotNull(propertySyntax.ExpressionBody);
Assert.NotEqual(SyntaxKind.None, propertySyntax.ExpressionBody.ArrowToken.Kind());
Assert.NotNull(propertySyntax.ExpressionBody.Expression);
Assert.Equal(SyntaxKind.SemicolonToken, propertySyntax.SemicolonToken.Kind());
}
[Fact]
public void TestStructGetterPropertyWithReadonly()
{
var text = "struct a { int P { readonly get { return 42; } } }";
var file = this.ParseFile(text);
Assert.NotNull(file);
Assert.Equal(1, file.Members.Count);
Assert.Equal(text, file.ToString());
Assert.Equal(0, file.Errors().Length);
Assert.Equal(SyntaxKind.StructDeclaration, file.Members[0].Kind());
var structDecl = (TypeDeclarationSyntax)file.Members[0];
Assert.Equal(0, structDecl.AttributeLists.Count);
Assert.Equal(0, structDecl.Modifiers.Count);
Assert.NotNull(structDecl.Keyword);
Assert.Equal(SyntaxKind.StructKeyword, structDecl.Keyword.Kind());
Assert.NotNull(structDecl.Identifier);
Assert.Equal("a", structDecl.Identifier.ToString());
Assert.Null(structDecl.BaseList);
Assert.Equal(0, structDecl.ConstraintClauses.Count);
Assert.NotNull(structDecl.OpenBraceToken);
Assert.NotNull(structDecl.CloseBraceToken);
Assert.Equal(1, structDecl.Members.Count);
Assert.Equal(SyntaxKind.PropertyDeclaration, structDecl.Members[0].Kind());
var propertySyntax = (PropertyDeclarationSyntax)structDecl.Members[0];
Assert.Equal(0, propertySyntax.AttributeLists.Count);
Assert.Equal(0, propertySyntax.Modifiers.Count);
Assert.NotNull(propertySyntax.Type);
Assert.Equal("int", propertySyntax.Type.ToString());
Assert.NotNull(propertySyntax.Identifier);
Assert.Equal("P", propertySyntax.Identifier.ToString());
var accessors = propertySyntax.AccessorList.Accessors;
Assert.Equal(1, accessors.Count);
Assert.Equal(1, accessors[0].Modifiers.Count);
Assert.Equal(SyntaxKind.ReadOnlyKeyword, accessors[0].Modifiers[0].Kind());
}
[Fact]
public void TestClassMethodWithParameter()
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册