diff --git a/TUnit.Analyzers.Tests/DisposableFieldPropertyAnalyzerTests.cs b/TUnit.Analyzers.Tests/DisposableFieldPropertyAnalyzerTests.cs index 4b545d6757..ca52aaa8e1 100644 --- a/TUnit.Analyzers.Tests/DisposableFieldPropertyAnalyzerTests.cs +++ b/TUnit.Analyzers.Tests/DisposableFieldPropertyAnalyzerTests.cs @@ -1197,4 +1197,92 @@ public void Test1() """ ); } + + // ======================================== + // FUNC SHOULD NOT BE FLAGGED + // ======================================== + + [Test] + public async Task Func_Returning_Disposable_No_Issue() + { + await Verifier + .VerifyAnalyzerAsync( + """ + using System; + using System.Net.Http; + using TUnit.Core; + + public interface IMyInterface : IDisposable + { + } + + public class MyClass : IMyInterface + { + public void Dispose() + { + Console.WriteLine("disposed"); + } + } + + public class ExampleTest + { + private readonly Func _factory = () => new MyClass(); + + [Test] + public void Test1() + { + using var t = _factory(); + } + } + """ + ); + } + + [Test] + public async Task Func_Returning_HttpClient_No_Issue() + { + await Verifier + .VerifyAnalyzerAsync( + """ + using System; + using System.Net.Http; + using TUnit.Core; + + public class DisposableFieldTests + { + private readonly Func _clientFactory = () => new HttpClient(); + + [Test] + public void Test1() + { + using var client = _clientFactory(); + } + } + """ + ); + } + + [Test] + public async Task Property_With_Func_Returning_Disposable_No_Issue() + { + await Verifier + .VerifyAnalyzerAsync( + """ + using System; + using System.Net.Http; + using TUnit.Core; + + public class DisposableFieldTests + { + private Func ClientFactory { get; } = () => new HttpClient(); + + [Test] + public void Test1() + { + using var client = ClientFactory(); + } + } + """ + ); + } } diff --git a/TUnit.Analyzers/DisposableFieldPropertyAnalyzer.cs b/TUnit.Analyzers/DisposableFieldPropertyAnalyzer.cs index 151d20de8c..85d9e921b2 100644 --- a/TUnit.Analyzers/DisposableFieldPropertyAnalyzer.cs +++ b/TUnit.Analyzers/DisposableFieldPropertyAnalyzer.cs @@ -112,7 +112,9 @@ private static void CheckFieldInitializers(SyntaxNodeAnalysisContext context, IN if (typeInfo.Type?.IsDisposable() is true || typeInfo.Type?.IsAsyncDisposable() is true) { var fieldSymbol = context.SemanticModel.GetDeclaredSymbol(variable) as IFieldSymbol; - if (fieldSymbol != null) + // Only flag if the field type itself is disposable (not e.g. Func) + if (fieldSymbol != null && + (fieldSymbol.Type?.IsDisposable() is true || fieldSymbol.Type?.IsAsyncDisposable() is true)) { createdObjects.TryAdd(fieldSymbol, HookLevel.Test); break; // Only need to add once @@ -145,7 +147,9 @@ private static void CheckFieldInitializers(SyntaxNodeAnalysisContext context, IN if (typeInfo.Type?.IsDisposable() is true || typeInfo.Type?.IsAsyncDisposable() is true) { var propertySymbol = context.SemanticModel.GetDeclaredSymbol(propertyDeclaration) as IPropertySymbol; - if (propertySymbol != null) + // Only flag if the property type itself is disposable (not e.g. Func) + if (propertySymbol != null && + (propertySymbol.Type?.IsDisposable() is true || propertySymbol.Type?.IsAsyncDisposable() is true)) { createdObjects.TryAdd(propertySymbol, HookLevel.Test); break; // Only need to add once