diff --git a/TUnit.Analyzers.Tests/DisposableFieldPropertyAnalyzerTests.cs b/TUnit.Analyzers.Tests/DisposableFieldPropertyAnalyzerTests.cs index ca52aaa8e1..4767c70668 100644 --- a/TUnit.Analyzers.Tests/DisposableFieldPropertyAnalyzerTests.cs +++ b/TUnit.Analyzers.Tests/DisposableFieldPropertyAnalyzerTests.cs @@ -1285,4 +1285,75 @@ public void Test1() """ ); } + + [Test] + public async Task Constructor_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; + + public ExampleTest() + { + _factory = () => new MyClass(); + } + + [Test] + public void Test1() + { + using var t = _factory(); + } + } + """ + ); + } + + [Test] + public async Task BeforeTest_Func_Returning_Disposable_No_Issue() + { + await Verifier + .VerifyAnalyzerAsync( + """ + using System; + using System.Net.Http; + using TUnit.Core; + + public class DisposableFieldTests + { + private Func? _clientFactory; + + [Before(HookType.Test)] + public void Setup() + { + _clientFactory = () => new HttpClient(); + } + + [Test] + public void Test1() + { + using var client = _clientFactory!(); + } + } + """ + ); + } } diff --git a/TUnit.Analyzers/DisposableFieldPropertyAnalyzer.cs b/TUnit.Analyzers/DisposableFieldPropertyAnalyzer.cs index 85d9e921b2..6c9c75d980 100644 --- a/TUnit.Analyzers/DisposableFieldPropertyAnalyzer.cs +++ b/TUnit.Analyzers/DisposableFieldPropertyAnalyzer.cs @@ -205,14 +205,18 @@ private static void CheckSetUps(SyntaxNodeAnalysisContext context, IMethodSymbol .OfType() .Any(x => x.Type?.IsDisposable() is true || x.Type?.IsAsyncDisposable() is true)) { + // Only flag if the field type itself is disposable (not e.g. Func) if (assignmentOperation.Target is IFieldReferenceOperation fieldReferenceOperation - && context.Compilation.HasImplicitConversion(methodSymbol.ContainingType, fieldReferenceOperation.Field.ContainingType)) + && context.Compilation.HasImplicitConversion(methodSymbol.ContainingType, fieldReferenceOperation.Field.ContainingType) + && (fieldReferenceOperation.Field.Type?.IsDisposable() is true || fieldReferenceOperation.Field.Type?.IsAsyncDisposable() is true)) { createdObjects.TryAdd(fieldReferenceOperation.Field, level); } + // Only flag if the property type itself is disposable (not e.g. Func) if (assignmentOperation.Target is IPropertyReferenceOperation propertyReferenceOperation - && context.Compilation.HasImplicitConversion(methodSymbol.ContainingType, propertyReferenceOperation.Property.ContainingType)) + && context.Compilation.HasImplicitConversion(methodSymbol.ContainingType, propertyReferenceOperation.Property.ContainingType) + && (propertyReferenceOperation.Property.Type?.IsDisposable() is true || propertyReferenceOperation.Property.Type?.IsAsyncDisposable() is true)) { createdObjects.TryAdd(propertyReferenceOperation.Property, level); }