Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
9d64678
feat(analyzers): adopt SecuritySharp.Analyzers and update the analyze…
glennawatson Jul 20, 2026
3708bee
fix(analyzers): update to 3.30.0 and clear the CI analyzer failures
glennawatson Jul 20, 2026
7cfe5df
build(deps): update RoslynCommonAnalyzers to 3.30.1
glennawatson Jul 20, 2026
69857c1
fix(concurrency): restore public Run and reorder WasmScheduler dispos…
glennawatson Jul 20, 2026
b22d423
build(deps): update RoslynCommonAnalyzers to 3.30.2
glennawatson Jul 20, 2026
063e364
build(deps): update RoslynCommonAnalyzers to 3.30.3
glennawatson Jul 20, 2026
8cc5625
chore(editorconfig): disable Sonar rules covered in-box or obsolete
glennawatson Jul 20, 2026
7f20375
docs(editorconfig): note which rule supersedes each disabled Sonar rule
glennawatson Jul 20, 2026
ba9b457
build(deps): update RoslynCommonAnalyzers to 3.31.0
glennawatson Jul 20, 2026
702ff18
chore(editorconfig): enable the remaining 3.31.0 platform rules
glennawatson Jul 20, 2026
501fddb
build(deps): update RoslynCommonAnalyzers to 3.32.0
glennawatson Jul 20, 2026
e0c355e
build: replace SonarAnalyzer rules with RoslynCommonAnalyzers 3.33.0
glennawatson Jul 20, 2026
8f9208d
test: add unit tests for Sequencer and ThreadPoolSequencer functionality
glennawatson Jul 20, 2026
84d9592
build: adjust severity levels for specific analyzers in .editorconfig
glennawatson Jul 20, 2026
42e62db
refactor: remove unnecessary SuppressMessage attributes and improve c…
glennawatson Jul 20, 2026
c6e4f48
refactor: simplify summary documentation for SequencerTests class
glennawatson Jul 20, 2026
aa479bd
refactor: simplify summary documentation for SequencerTests class
glennawatson Jul 20, 2026
3637e2a
test: stop DelayUntil racing the wall clock
glennawatson Jul 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
649 changes: 398 additions & 251 deletions .editorconfig

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@
<PackageReference Include="MinVer" PrivateAssets="all"/>
<PackageReference Include="StyleSharp.Analyzers" PrivateAssets="all"/>
<PackageReference Include="PerformanceSharp.Analyzers" PrivateAssets="all"/>
<PackageReference Include="SecuritySharp.Analyzers" PrivateAssets="all"/>
<PackageReference Include="Roslynator.Analyzers" PrivateAssets="All"/>
<PackageReference Include="SonarAnalyzer.CSharp" PrivateAssets="all"/>
</ItemGroup>
Expand Down
6 changes: 4 additions & 2 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
<!-- StyleSharp.Analyzers and PerformanceSharp.Analyzers ship from the same release pipeline and always share a version. -->
<RoslynCommonAnalyzersVersion>3.28.1</RoslynCommonAnalyzersVersion>
<!-- StyleSharp.Analyzers, PerformanceSharp.Analyzers and SecuritySharp.Analyzers ship from the
same release pipeline and always share a version. -->
<RoslynCommonAnalyzersVersion>3.33.0</RoslynCommonAnalyzersVersion>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.301"/>
Expand All @@ -13,6 +14,7 @@
<PackageVersion Include="MinVer" Version="7.0.0"/>
<PackageVersion Include="StyleSharp.Analyzers" Version="$(RoslynCommonAnalyzersVersion)"/>
<PackageVersion Include="PerformanceSharp.Analyzers" Version="$(RoslynCommonAnalyzersVersion)"/>
<PackageVersion Include="SecuritySharp.Analyzers" Version="$(RoslynCommonAnalyzersVersion)"/>
<PackageVersion Include="Roslynator.Analyzers" Version="4.15.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="5.6.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="5.6.0"/>
Expand Down
2 changes: 1 addition & 1 deletion src/Polyfills/ArgumentExceptionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal static class ArgumentExceptionHelper
/// <summary>Throws an <see cref="ArgumentNullException"/> if <paramref name="argument"/> is <see langword="null"/>.</summary>
/// <param name="argument">The reference type argument to validate as non-null.</param>
/// <param name="paramName">The name of the parameter with which <paramref name="argument"/> corresponds.</param>
public static void ThrowIfNull(
internal static void ThrowIfNull(
[NotNull] object? argument,
[CallerArgumentExpression(nameof(argument))]
string? paramName = null)
Expand Down
10 changes: 5 additions & 5 deletions src/Polyfills/ArgumentOutOfRangeExceptionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal static class ArgumentOutOfRangeExceptionHelper
/// <summary>Throws when <paramref name="value"/> is negative.</summary>
/// <param name="value">The value to validate.</param>
/// <param name="paramName">The parameter name.</param>
public static void ThrowIfNegative(int value, [CallerArgumentExpression(nameof(value))] string? paramName = null)
internal static void ThrowIfNegative(int value, [CallerArgumentExpression(nameof(value))] string? paramName = null)
{
if (value >= 0)
{
Expand All @@ -27,7 +27,7 @@ public static void ThrowIfNegative(int value, [CallerArgumentExpression(nameof(v
/// <summary>Throws when <paramref name="value"/> is negative or zero.</summary>
/// <param name="value">The value to validate.</param>
/// <param name="paramName">The parameter name.</param>
public static void ThrowIfNegativeOrZero(
internal static void ThrowIfNegativeOrZero(
int value,
[CallerArgumentExpression(nameof(value))]
string? paramName = null)
Expand All @@ -44,7 +44,7 @@ public static void ThrowIfNegativeOrZero(
/// <param name="value">The value to validate.</param>
/// <param name="other">The lower bound.</param>
/// <param name="paramName">The parameter name.</param>
public static void ThrowIfLessThan(
internal static void ThrowIfLessThan(
int value,
int other,
[CallerArgumentExpression(nameof(value))]
Expand All @@ -69,7 +69,7 @@ public static void ThrowIfLessThan(
"A type-specialized polyfill overload. The bounds-check body is identical to the int overload only "
+ "because the guard shape is the same; the two operate on different value types (int vs TimeSpan) and "
+ "cannot forward to one another. This mirrors the BCL's per-type ThrowIfLessThan overloads.")]
public static void ThrowIfLessThan(
internal static void ThrowIfLessThan(
TimeSpan value,
TimeSpan other,
[CallerArgumentExpression(nameof(value))]
Expand All @@ -87,7 +87,7 @@ public static void ThrowIfLessThan(
/// <param name="value">The value to validate.</param>
/// <param name="other">The lower bound.</param>
/// <param name="paramName">The parameter name.</param>
public static void ThrowIfLessThanOrEqual(
internal static void ThrowIfLessThanOrEqual(
TimeSpan value,
TimeSpan other,
[CallerArgumentExpression(nameof(value))]
Expand Down
2 changes: 1 addition & 1 deletion src/Polyfills/CallerArgumentExpressionAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ public CallerArgumentExpressionAttribute(string parameterName) =>
ParameterName = parameterName;

/// <summary>Gets the name of the parameter whose expression should be captured.</summary>
public string ParameterName { get; }
internal string ParameterName { get; }
}
#endif
6 changes: 3 additions & 3 deletions src/Polyfills/CancellationTokenPolyfillExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ internal static class CancellationTokenPolyfillExtensions
/// <param name="callback">The delegate to invoke on cancellation.</param>
/// <param name="state">The state passed to <paramref name="callback"/>.</param>
/// <returns>A registration that can be disposed to remove the callback.</returns>
public CancellationTokenRegistration UnsafeRegister(Action<object?> callback, object? state) =>
internal CancellationTokenRegistration UnsafeRegister(Action<object?> callback, object? state) =>
token.Register(callback, state, false);

/// <summary>Registers a delegate that is invoked with the triggering token when the token is cancelled, without capturing the execution context.</summary>
/// <param name="callback">The delegate to invoke on cancellation, receiving the state and the triggering token.</param>
/// <param name="state">The state passed to <paramref name="callback"/>.</param>
/// <returns>A registration that can be disposed to remove the callback.</returns>
public CancellationTokenRegistration
internal CancellationTokenRegistration
UnsafeRegister(Action<object?, CancellationToken> callback, object? state) =>
token.Register(
static boxed =>
Expand All @@ -44,7 +44,7 @@ public CancellationTokenRegistration
{
/// <summary>Removes the registered callback; on this framework the removal may wait for an in-flight callback.</summary>
/// <returns><see langword="true"/> when the registration was released.</returns>
public bool Unregister()
internal bool Unregister()
{
registration.Dispose();
return true;
Expand Down
2 changes: 1 addition & 1 deletion src/Polyfills/CancellationTokenSourcePolyfillExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal static class CancellationTokenSourcePolyfillExtensions
{
/// <summary>Communicates a request for cancellation, completing synchronously (no asynchronous callback draining on this framework).</summary>
/// <returns>A completed task representing the cancellation request.</returns>
public Task CancelAsync()
internal Task CancelAsync()
{
source.Cancel();
return Task.CompletedTask;
Expand Down
6 changes: 3 additions & 3 deletions src/Polyfills/CompilerFeatureRequiredAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ namespace System.Runtime.CompilerServices;
internal sealed class CompilerFeatureRequiredAttribute : Attribute
{
/// <summary>The <see cref="FeatureName"/> used for the ref structs C# feature.</summary>
public const string RefStructs = nameof(RefStructs);
internal const string RefStructs = nameof(RefStructs);

/// <summary>The <see cref="FeatureName"/> used for the required members C# feature.</summary>
public const string RequiredMembers = nameof(RequiredMembers);
internal const string RequiredMembers = nameof(RequiredMembers);

/// <summary>Initializes a new instance of the <see cref="CompilerFeatureRequiredAttribute"/> class.</summary>
/// <param name="featureName">The name of the required compiler feature.</param>
public CompilerFeatureRequiredAttribute(string featureName) =>
FeatureName = featureName;

/// <summary>Gets the name of the required compiler feature.</summary>
public string FeatureName { get; }
internal string FeatureName { get; }
}
#endif
4 changes: 2 additions & 2 deletions src/Polyfills/MemberNotNullWhenAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
}

/// <summary>Gets the return value condition for which the members are not <see langword="null"/>.</summary>
public bool ReturnValue { get; }
internal bool ReturnValue { get; }

/// <summary>Gets the field and property member names that are promised to be not-<see langword="null"/>.</summary>
public string[] Members { get; }
internal string[] Members { get; }
}
#endif
2 changes: 1 addition & 1 deletion src/Polyfills/NotNullWhenAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ public NotNullWhenAttribute(bool returnValue) =>
ReturnValue = returnValue;

/// <summary>Gets the return value condition for which the parameter is not <see langword="null"/>.</summary>
public bool ReturnValue { get; }
internal bool ReturnValue { get; }
}
#endif
2 changes: 1 addition & 1 deletion src/Polyfills/ObjectDisposedExceptionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal static class ObjectDisposedExceptionHelper
/// <summary>Throws an <see cref="ObjectDisposedException"/> if <paramref name="condition"/> is <see langword="true"/>.</summary>
/// <param name="condition">The condition to evaluate for a disposed instance.</param>
/// <param name="instance">The object whose type name is used to build the exception message.</param>
public static void ThrowIf(bool condition, object instance)
internal static void ThrowIf(bool condition, object instance)
{
if (!condition)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Polyfills/OverloadResolutionPriorityAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ namespace System.Runtime.CompilerServices;
internal sealed class OverloadResolutionPriorityAttribute(int priority) : Attribute
{
/// <summary>Gets the overload resolution priority.</summary>
public int Priority { get; } = priority;
internal int Priority { get; } = priority;
}
#endif
14 changes: 7 additions & 7 deletions src/Polyfills/TaskCompletionSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,23 @@ internal sealed class TaskCompletionSource
public TaskCompletionSource() => _inner = new();

/// <summary>Transitions the underlying task to the <see cref="TaskStatus.RanToCompletion"/> state.</summary>
public void SetResult() => _inner.SetResult(true);
internal void SetResult() => _inner.SetResult(true);

/// <summary>Attempts to transition the underlying task to the <see cref="TaskStatus.RanToCompletion"/> state.</summary>
/// <returns><see langword="true"/> if the operation was successful; otherwise <see langword="false"/>.</returns>
public bool TrySetResult() => _inner.TrySetResult(true);
internal bool TrySetResult() => _inner.TrySetResult(true);

/// <summary>Transitions the underlying task to the <see cref="TaskStatus.Faulted"/> state with the specified exception.</summary>
/// <param name="exception">The exception to bind to the task.</param>
public void SetException(Exception exception) => _inner.SetException(exception);
internal void SetException(Exception exception) => _inner.SetException(exception);

/// <summary>Attempts to transition the underlying task to the <see cref="TaskStatus.Faulted"/> state with the specified exception.</summary>
/// <param name="exception">The exception to bind to the task.</param>
/// <returns><see langword="true"/> if the operation was successful; otherwise <see langword="false"/>.</returns>
public bool TrySetException(Exception exception) => _inner.TrySetException(exception);
internal bool TrySetException(Exception exception) => _inner.TrySetException(exception);

/// <summary>Transitions the underlying task to the <see cref="TaskStatus.Canceled"/> state.</summary>
public void SetCanceled() => _inner.TrySetCanceled();
internal void SetCanceled() => _inner.TrySetCanceled();

/// <summary>Attempts to transition the underlying task to the <see cref="TaskStatus.Canceled"/> state.</summary>
/// <returns><see langword="true"/> if the operation was successful; otherwise <see langword="false"/>.</returns>
Expand All @@ -50,11 +50,11 @@ internal sealed class TaskCompletionSource
"Distinct BCL-parity surface members with different contracts and return types: SetCanceled is void and "
+ "TrySetCanceled returns bool. They share the same backing call here but must track the framework surface "
+ "independently, so they are deliberately kept as separate members rather than one forwarding to the other.")]
public bool TrySetCanceled() => _inner.TrySetCanceled();
internal bool TrySetCanceled() => _inner.TrySetCanceled();

/// <summary>Attempts to transition the underlying task to the <see cref="TaskStatus.Canceled"/> state for the specified token.</summary>
/// <param name="cancellationToken">The token associated with the cancellation.</param>
/// <returns><see langword="true"/> if the operation was successful; otherwise <see langword="false"/>.</returns>
public bool TrySetCanceled(CancellationToken cancellationToken) => _inner.TrySetCanceled(cancellationToken);
internal bool TrySetCanceled(CancellationToken cancellationToken) => _inner.TrySetCanceled(cancellationToken);
}
#endif
4 changes: 2 additions & 2 deletions src/Polyfills/TaskPolyfillExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal static class TaskPolyfillExtensions
/// <param name="timeout">The timeout after which the returned task faults, or <see cref="Timeout.InfiniteTimeSpan"/> for no timeout.</param>
/// <param name="cancellationToken">A token that cancels the wait.</param>
/// <returns>A task that mirrors <paramref name="task"/> subject to the timeout and cancellation.</returns>
public async Task WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)
internal async Task WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
await WaitForCompletionAsync(task, timeout, cancellationToken).ConfigureAwait(false);
await task.ConfigureAwait(false);
Expand All @@ -34,7 +34,7 @@ public async Task WaitAsync(TimeSpan timeout, CancellationToken cancellationToke
/// <param name="timeout">The timeout after which the returned task faults, or <see cref="Timeout.InfiniteTimeSpan"/> for no timeout.</param>
/// <param name="cancellationToken">A token that cancels the wait.</param>
/// <returns>A task that mirrors <paramref name="task"/> subject to the timeout and cancellation.</returns>
public async Task<T> WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)
internal async Task<T> WaitAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
await WaitForCompletionAsync(task, timeout, cancellationToken).ConfigureAwait(false);
return await task.ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal static class InvalidOperationExceptionHelper
/// <param name="argument">The reference type field to validate as non-null.</param>
/// <param name="memberName">The validated member's name, captured from the <paramref name="argument"/> expression via <see cref="CallerArgumentExpressionAttribute"/>.</param>
/// <param name="operation">The void-throwing caller's name, captured via <see cref="CallerMemberNameAttribute"/>.</param>
public static void ThrowIfNull(
internal static void ThrowIfNull(
[NotNull] object? argument,
[CallerArgumentExpression(nameof(argument))]
string? memberName = null,
Expand All @@ -49,7 +49,7 @@ public static void ThrowIfNull(
/// <param name="memberName">The validated reference-type argument's name, captured from the <paramref name="argument"/> expression via <see cref="CallerArgumentExpressionAttribute"/>.</param>
/// <param name="operation">The reference-type-checking caller's name, captured via <see cref="CallerMemberNameAttribute"/>.</param>
/// <returns>The non-null argument.</returns>
public static T Check<T>(
internal static T Check<T>(
[NotNull] T? argument,
[CallerArgumentExpression(nameof(argument))]
string? memberName = null,
Expand All @@ -73,7 +73,7 @@ public static T Check<T>(
/// <param name="memberName">The validated string argument's name, captured from the <paramref name="argument"/> expression via <see cref="CallerArgumentExpressionAttribute"/>.</param>
/// <param name="operation">The string-checking caller's name, captured via <see cref="CallerMemberNameAttribute"/>.</param>
/// <returns>The non-null, non-empty argument.</returns>
public static string Check(
internal static string Check(
[NotNull] string? argument,
[CallerArgumentExpression(nameof(argument))]
string? memberName = null,
Expand Down
Loading
Loading