From 9a89e3e3705f52e7b04b005cb716753181f9e62b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 22:35:01 +0000 Subject: [PATCH 1/2] Initial plan From d517a9d058af5a7ffae740fd42d14f701c752ea2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 22:45:53 +0000 Subject: [PATCH 2/2] Fix VSTHRD114: detect null in ternary/conditional expressions Co-authored-by: AArnott <3548+AArnott@users.noreply.github.com> --- ...VSTHRD114AvoidReturningNullTaskAnalyzer.cs | 39 ++++++++++++---- ...D114AvoidReturningNullTaskAnalyzerTests.cs | 45 ++++++++++++++++++- 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD114AvoidReturningNullTaskAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD114AvoidReturningNullTaskAnalyzer.cs index dcd2e9425..b677d1bfe 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD114AvoidReturningNullTaskAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/AbstractVSTHRD114AvoidReturningNullTaskAnalyzer.cs @@ -55,19 +55,42 @@ public override void Initialize(AnalysisContext context) }; } + private static void CheckForNullValue(OperationAnalysisContext context, IOperation operation) + { + if (operation is IConditionalOperation conditionalOp) + { + if (conditionalOp.WhenTrue is { } whenTrue) + { + CheckForNullValue(context, whenTrue); + } + + if (conditionalOp.WhenFalse is { } whenFalse) + { + CheckForNullValue(context, whenFalse); + } + } + else if (operation.ConstantValue is { HasValue: true, Value: null } && + operation.Syntax is { } nullSyntax) + { + context.ReportDiagnostic(Diagnostic.Create(Descriptor, nullSyntax.GetLocation())); + } + } + private void AnalyzerReturnOperation(OperationAnalysisContext context) { var returnOperation = (IReturnOperation)context.Operation; - if (returnOperation.ReturnedValue is { ConstantValue: { HasValue: true, Value: null } } && // could be null for implicit returns - returnOperation.ReturnedValue.Syntax is { } returnedValueSyntax && - Utils.GetContainingFunctionBlock(returnOperation) is { } block && - FindOwningSymbol(block, context.ContainingSymbol) is { } method && - !method.IsAsync && - Utils.IsTask(method.ReturnType) && - !this.LanguageUtils.MethodReturnsNullableReferenceType(method)) + // ReturnedValue is null for implicit/void returns + if (returnOperation.ReturnedValue is not { } returnedValue || + Utils.GetContainingFunctionBlock(returnOperation) is not { } block || + FindOwningSymbol(block, context.ContainingSymbol) is not { } owningMethod || + owningMethod.IsAsync || + !Utils.IsTask(owningMethod.ReturnType) || + this.LanguageUtils.MethodReturnsNullableReferenceType(owningMethod)) { - context.ReportDiagnostic(Diagnostic.Create(Descriptor, returnedValueSyntax.GetLocation())); + return; } + + CheckForNullValue(context, returnedValue); } } diff --git a/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskAnalyzerTests.cs index 63822876f..5487ddbe6 100644 --- a/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD114AvoidReturningNullTaskAnalyzerTests.cs @@ -147,7 +147,7 @@ public Task GetTaskObj() } [Fact] - public async Task NullInTernary_NoDiagnostic_FalseNegative() + public async Task NullInTernary_Diagnostic() { var test = @" using System.Threading.Tasks; @@ -156,7 +156,7 @@ class Test { public Task GetTaskObj(bool b) { - return b ? default(Task) : null; + return b ? [|default(Task)|] : [|null|]; } } "; @@ -166,6 +166,47 @@ public Task GetTaskObj(bool b) }.RunAsync(); } + [Fact] + public async Task NullInTernaryReturnStatement_Diagnostic() + { + var csharpTest = @" +using System.Threading.Tasks; + +class Test +{ + public Task First(bool flag) + { + return flag + ? [|null|] + : Task.CompletedTask; + } + + public Task Second(bool flag) => + flag + ? [|null|] + : Task.CompletedTask; +} +"; + await new CSVerify.Test + { + TestCode = csharpTest, + }.RunAsync(); + + var vbTest = @" +Imports System.Threading.Tasks + +Friend Class Test + Public Function First(flag As Boolean) As Task + Return If(flag, [|Nothing|], Task.CompletedTask) + End Function +End Class +"; + await new VerifyVB.Test + { + TestCode = vbTest, + }.RunAsync(); + } + [Fact] public async Task MultipleFaultyReturns_MultipleDiagnostics() {