Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion docfx/analyzers/VSTHRD103.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,3 @@ Some APIs may have async versions that are less efficient or inappropriate for c

See our [configuration](configuration.md) topic to learn how to exclude specific methods
using the `vs-threading.SyncMethodsToExcludeFromVSTHRD103.txt` file.
```
16 changes: 15 additions & 1 deletion docfx/analyzers/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,28 @@ when in an async context. Sometimes certain APIs have async versions but those a
are significantly slower, less efficient, or simply not preferred. These methods can be
excluded from VSTHRD103 analysis by specifying them in a configuration file.

**Filename:** `vs-threading.SyncMethodsToExcludeFromVSTHRD103.txt`
**Filename:** `vs-threading.SyncMethodsToExcludeFromVSTHRD103.txt`, or
`vs-threading.SyncMethodsToExcludeFromVSTHRD103.<suffix>.txt`

**Line format:** `[Namespace.TypeName]::MethodName`

**Sample:** `[System.Data.SqlClient.SqlDataReader]::Read`

**Generic sample:** ``[Microsoft.EntityFrameworkCore.DbSet`1]::Add``

The analyzer package supplies default exclusions for APIs whose Async-suffixed alternatives
are intended for different operations or exceptional use cases:

| API | Excluded methods |
| --- | --- |
| Entity Framework Core `DbContext` | `Add`, `AddRange` |
| Entity Framework Core `DbSet<TEntity>` | `Add`, `AddRange` |
| NUnit `Assert` | `That` |
| xUnit `Assert` | `Throws`, `ThrowsAny` |

Projects and NuGet packages can extend this list by adding their own files that match the
filename pattern above.
Comment thread
AArnott marked this conversation as resolved.

## Additional synchronous blocking methods for VSTHRD002

Projects that wrap synchronous waits in their own APIs can configure those methods to be
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# These APIs have Async-suffixed alternatives that are not generally equivalent to or preferred over their synchronous counterparts.
[Microsoft.EntityFrameworkCore.DbContext]::Add
[Microsoft.EntityFrameworkCore.DbContext]::AddRange
[Microsoft.EntityFrameworkCore.DbSet`1]::Add
[Microsoft.EntityFrameworkCore.DbSet`1]::AddRange
[NUnit.Framework.Assert]::That
[Xunit.Assert]::Throws
[Xunit.Assert]::ThrowsAny
Original file line number Diff line number Diff line change
Expand Up @@ -2649,6 +2649,87 @@ public static void SlowSyncMethod() { }
await CSVerify.VerifyAnalyzerAsync(test);
}

[Fact]
public async Task KnownFrameworkMethodsWithNonEquivalentAsyncAlternativesDoNotGenerateWarning()
{
string test = """
using System;
using System.Threading.Tasks;

class Test
{
async Task TestAsync()
{
Xunit.Assert.Throws<Exception>(() => { });
Xunit.Assert.ThrowsAny<Exception>(() => { });
NUnit.Framework.Assert.That(true, true);

var context = new Microsoft.EntityFrameworkCore.DbContext();
context.Add(new object());
context.AddRange(new object());

var set = new Microsoft.EntityFrameworkCore.DbSet<object>();
set.Add(new object());
set.AddRange(new object());

Unrelated.Assert.{|#0:Throws<Exception>|}(() => { });
}
}

namespace Xunit
{
static class Assert
{
public static void Throws<T>(Action action) { }
public static Task ThrowsAsync<T>(Action action) => Task.CompletedTask;
public static void ThrowsAny<T>(Action action) { }
public static Task ThrowsAnyAsync<T>(Action action) => Task.CompletedTask;
}
}

namespace NUnit.Framework
{
static class Assert
{
public static void That<T>(T actual, T expected) { }
public static Task ThatAsync<T>(T actual, T expected) => Task.CompletedTask;
}
}

namespace Microsoft.EntityFrameworkCore
{
class DbContext
{
public void Add(object entity) { }
public Task AddAsync(object entity) => Task.CompletedTask;
public void AddRange(params object[] entities) { }
public Task AddRangeAsync(params object[] entities) => Task.CompletedTask;
}

class DbSet<T>
{
public void Add(T entity) { }
public Task AddAsync(T entity) => Task.CompletedTask;
public void AddRange(params T[] entities) { }
public Task AddRangeAsync(params T[] entities) => Task.CompletedTask;
}
}

namespace Unrelated
{
static class Assert
{
public static void Throws<T>(Action action) { }
public static Task ThrowsAsync<T>(Action action) => Task.CompletedTask;
}
}
""";

await CSVerify.VerifyAnalyzerAsync(
test,
CSVerify.Diagnostic(Descriptor).WithLocation(0).WithArguments("Throws<Exception>", "ThrowsAsync"));
}

[Fact]
public async Task GenericTypeExclusion_DoesNotExcludeNonGenericType()
{
Expand Down
Loading