feat: implement analyzer and code fixer for identifying and fixing when a sync flag is not propagated correctly.

This commit is contained in:
Stevan Freeborn
2026-06-18 21:28:41 -05:00
parent 86b9673b24
commit f3da2f60fd
45 changed files with 2184 additions and 0 deletions
@@ -0,0 +1,10 @@
root = true
[*.cs]
dotnet_diagnostic.SYNC001.additional_sync_names = runSynchronously, isSync
dotnet_diagnostic.CA2007.severity = none
dotnet_diagnostic.CA1707.severity = none
indent_size = 2
tab_width = 2
@@ -0,0 +1,70 @@
internal static class Program
{
public static async Task Main()
{
await Task.CompletedTask;
}
}
internal interface IDataService
{
Task<string> GetDataAsync(string name, bool sync = false);
Task<string> GetMoreDataAsync(bool runSynchronously, string name);
Task<string> FetchAsync(int id, bool sync = false, bool cache = true);
}
internal class BusinessLogic(IDataService dataService)
{
private readonly IDataService _dataService = dataService;
// ── Good patterns (no SYNC001) ──────────────────────────
/// <summary>Passes the default 'sync' variable positionally.</summary>
public async Task<string> Good_ForwardSync(bool sync)
{
return await _dataService.GetDataAsync("Stevan", sync);
}
/// <summary>Passes a custom 'runSynchronously' variable positionally.</summary>
public async Task<string> Good_ForwardCustomFlag(bool runSynchronously)
{
return await _dataService.GetMoreDataAsync(runSynchronously, "Freeborn");
}
// ── Bad patterns (SYNC001, demonstrating code fix) ──────
/// <summary>Omits the sync argument entirely.
/// Code fix: inserts `sync` at ordinal 1.</summary>
public async Task<string> Bad_OmittedSync(bool sync)
{
return await _dataService.GetDataAsync("Stevan");
}
/// <summary>Hardcodes `false` as a positional argument.
/// Code fix: replaces arg at ordinal 1 with `sync`.</summary>
public async Task<string> Bad_HardcodedValue(bool sync)
{
return await _dataService.GetDataAsync("Stevan", false);
}
/// <summary>Hardcodes a value using a named argument.
/// Code fix: replaces expression in the named arg, keeping `sync:`.</summary>
public async Task<string> Bad_NamedHardcoded(bool sync)
{
return await _dataService.GetDataAsync("Stevan", sync: false);
}
/// <summary>Omits sync when it's not the last parameter.
/// Code fix: inserts `sync` at ordinal 1 (before `cache`).</summary>
public async Task<string> Bad_OmittedSyncNotLast(bool sync)
{
return await _dataService.FetchAsync(17);
}
/// <summary>Hardcodes `false` at ordinal 0 where the target expects `runSynchronously`.
/// Code fix: replaces arg at ordinal 0 with `sync` (value = enclosing name).</summary>
public async Task<string> Bad_HardcodedAtOrdinalZero(bool sync)
{
return await _dataService.GetMoreDataAsync(false, "Freeborn");
}
}
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net11.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\StevanFreeborn.AsyncSyncFlagAnalyzer\StevanFreeborn.AsyncSyncFlagAnalyzer.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\src\StevanFreeborn.AsyncSyncFlagAnalyzer.CodeFixes\StevanFreeborn.AsyncSyncFlagAnalyzer.CodeFixes.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>