Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not return nonexistent kinds in SyntaxFacts' methods #72264

Merged
merged 11 commits into from
Mar 22, 2024
Merged
6 changes: 6 additions & 0 deletions src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ public enum SyntaxKind : ushort
/// <summary>Represents <c>..</c> token.</summary>
DotDotToken = 8222,

// Values ranging from 8193 (TildeToken) to 8287 (GreaterThanGreaterThanGreaterThanEqualsToken) are reserved for punctuation kinds.
// This gap is included within that range. So if you add a value here make sure `SyntaxFacts.GetPunctuationKinds` includes it in the returned enumeration

// additional xml tokens
/// <summary>Represents <c>/&gt;</c> token.</summary>
SlashGreaterThanToken = 8232, // xml empty element end
Expand All @@ -95,6 +98,9 @@ public enum SyntaxKind : ushort
/// <summary>Represents <c>?&gt;</c> token.</summary>
XmlProcessingInstructionEndToken = 8239, // ?>

// Values ranging from 8193 (TildeToken) to 8287 (GreaterThanGreaterThanGreaterThanEqualsToken) are reserved for punctuation kinds.
// This gap is included within that range. So if you add a value here make sure `SyntaxFacts.GetPunctuationKinds` includes it in the returned enumeration

// compound punctuation
/// <summary>Represents <c>||</c> token.</summary>
BarBarToken = 8260,
Expand Down
31 changes: 28 additions & 3 deletions src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace Microsoft.CodeAnalysis.CSharp
{
Expand All @@ -17,6 +19,7 @@ public static IEnumerable<SyntaxKind> GetReservedKeywordKinds()
{
for (int i = (int)SyntaxKind.BoolKeyword; i <= (int)SyntaxKind.ImplicitKeyword; i++)
{
Debug.Assert(Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i));
DoctorKrolic marked this conversation as resolved.
Show resolved Hide resolved
yield return (SyntaxKind)i;
}
}
Expand Down Expand Up @@ -142,9 +145,10 @@ public static IEnumerable<SyntaxKind> GetPreprocessorKeywordKinds()
yield return SyntaxKind.TrueKeyword;
yield return SyntaxKind.FalseKeyword;
yield return SyntaxKind.DefaultKeyword;
yield return SyntaxKind.HiddenKeyword;

for (int i = (int)SyntaxKind.ElifKeyword; i <= (int)SyntaxKind.RestoreKeyword; i++)
Copy link
Contributor Author

@DoctorKrolic DoctorKrolic Mar 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The [SyntaxKind.ElifKeyword - SyntaxKind.RestoreKeyword] range already contains SyntaxKind.HiddenKeyword, so I removed the duplicate. Found while working on tests

{
Debug.Assert(Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i));
yield return (SyntaxKind)i;
}
}
Expand Down Expand Up @@ -172,10 +176,26 @@ private static bool IsDebuggerSpecialPunctuation(SyntaxKind kind)

public static IEnumerable<SyntaxKind> GetPunctuationKinds()
{
for (int i = (int)SyntaxKind.TildeToken; i <= (int)SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken; i++)
for (int i = (int)SyntaxKind.TildeToken; i <= (int)SyntaxKind.DotDotToken; i++)
DoctorKrolic marked this conversation as resolved.
Show resolved Hide resolved
{
Debug.Assert(Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i));
yield return (SyntaxKind)i;
}

for (int i = (int)SyntaxKind.SlashGreaterThanToken; i <= (int)SyntaxKind.XmlProcessingInstructionEndToken; i++)
{
Debug.Assert(Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i));
yield return (SyntaxKind)i;
}

for (int i = (int)SyntaxKind.BarBarToken; i <= (int)SyntaxKind.QuestionQuestionEqualsToken; i++)
{
Debug.Assert(Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i));
yield return (SyntaxKind)i;
}

yield return SyntaxKind.GreaterThanGreaterThanGreaterThanToken;
yield return SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken;
}

public static bool IsPunctuationOrKeyword(SyntaxKind kind)
Expand Down Expand Up @@ -1148,7 +1168,12 @@ public static IEnumerable<SyntaxKind> GetContextualKeywordKinds()
{
for (int i = (int)SyntaxKind.YieldKeyword; i <= (int)SyntaxKind.FileKeyword; i++)
{
yield return (SyntaxKind)i;
// 8441 corresponds to a deleted kind (DataKeyword) that was previously shipped.
DoctorKrolic marked this conversation as resolved.
Show resolved Hide resolved
if (i != 8441)
{
Debug.Assert(Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i));
yield return (SyntaxKind)i;
}
}
}

Expand Down
66 changes: 66 additions & 0 deletions src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#nullable disable

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
Expand Down Expand Up @@ -276,5 +279,68 @@ public void IsAttributeTargetSpecifier()
}
}
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72300")]
public void TestAllKindsReturnedFromGetKindsMethodsExist()
DoctorKrolic marked this conversation as resolved.
Show resolved Hide resolved
{
foreach (var method in typeof(SyntaxFacts).GetMethods(BindingFlags.Public | BindingFlags.Static))
{
if (method.ReturnType == typeof(IEnumerable<SyntaxKind>) && method.GetParameters() is [])
{
foreach (var kind in (IEnumerable<SyntaxKind>)method.Invoke(null, null))
{
Assert.True(Enum.IsDefined(typeof(SyntaxKind), kind), $"Nonexistent kind '{kind}' returned from method '{method.Name}'");
}
}
}
}

[Theory, WorkItem("https://github.com/dotnet/roslyn/issues/72300")]
[InlineData(nameof(SyntaxFacts.GetContextualKeywordKinds), SyntaxKind.YieldKeyword, SyntaxKind.ElifKeyword)]
[InlineData(nameof(SyntaxFacts.GetPunctuationKinds), SyntaxKind.TildeToken, SyntaxKind.BoolKeyword)]
[InlineData(nameof(SyntaxFacts.GetReservedKeywordKinds), SyntaxKind.BoolKeyword, SyntaxKind.YieldKeyword)]
public void TestRangeBasedGetKindsMethodsReturnExpectedResults(string methodName, SyntaxKind lowerBoundInclusive, SyntaxKind upperBoundExclusive)
{
var method = typeof(SyntaxFacts).GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);

Assert.NotNull(method);
Assert.Equal(0, method.GetParameters().Length);
Assert.Equal(typeof(IEnumerable<SyntaxKind>), method.ReturnType);

var returnedKindsInts = ((IEnumerable<SyntaxKind>)method.Invoke(null, null)).Select(static k => (int)k).ToHashSet();

for (int i = (int)lowerBoundInclusive; i < (int)upperBoundExclusive; i++)
{
if (Enum.IsDefined(typeof(SyntaxKind), (SyntaxKind)i))
{
Assert.True(returnedKindsInts.Remove(i));
}
else
{
Assert.DoesNotContain(i, returnedKindsInts);
}
}

// We've already removed all expected kinds from the set. It should be empty now
Assert.Empty(returnedKindsInts);
}

[Fact]
public void TestGetPreprocessorKeywordKindsReturnsExpectedResults()
DoctorKrolic marked this conversation as resolved.
Show resolved Hide resolved
DoctorKrolic marked this conversation as resolved.
Show resolved Hide resolved
{
var returnedKindsInts = SyntaxFacts.GetPreprocessorKeywordKinds().Select(static k => (int)k).ToHashSet();

Assert.True(returnedKindsInts.Remove((int)SyntaxKind.TrueKeyword));
Assert.True(returnedKindsInts.Remove((int)SyntaxKind.FalseKeyword));
Assert.True(returnedKindsInts.Remove((int)SyntaxKind.DefaultKeyword));

for (int i = (int)SyntaxKind.ElifKeyword; i < (int)SyntaxKind.ReferenceKeyword; i++)
{
Assert.True(returnedKindsInts.Remove(i));
}

// We've already removed all expected kinds from the set. It should be empty now
Assert.Empty(returnedKindsInts);
}
}
}
12 changes: 12 additions & 0 deletions src/Compilers/VisualBasic/Test/Syntax/Syntax/SyntaxFactsTest.vb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
' See the LICENSE file in the project root for more information.

Imports System.IO
Imports System.Reflection
Imports System.Text
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.VisualBasic
Expand Down Expand Up @@ -1262,4 +1263,15 @@ End Module
Assert.Equal(isReserved, SyntaxFacts.IsReservedTupleElementName(elementName))
End Sub

<Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72300")>
Public Sub TestAllKindsReturnedFromGetKindsMethodsExist()
For Each method In GetType(SyntaxFacts).GetMethods(BindingFlags.Public Or BindingFlags.Static)
If method.ReturnType = GetType(IEnumerable(Of SyntaxKind)) AndAlso method.GetParameters().Length = 0 Then
For Each kind As SyntaxKind In DirectCast(method.Invoke(Nothing, Nothing), IEnumerable(Of SyntaxKind))
Assert.True([Enum].IsDefined(GetType(SyntaxKind), kind), $"Nonexistent kind '{kind}' returned from method '{method.Name}'")
Next
End If
Next
End Sub

End Class
Loading