diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 9331dab2b5c2e..0160a15534059 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -3193,6 +3193,11 @@ public override BoundNode VisitSequencePointWithSpan(BoundSequencePointWithSpan return null; } + public override BoundNode VisitModuleCancellationTokenExpression(ModuleCancellationTokenExpression node) + { + return null; + } + public override BoundNode VisitStatementList(BoundStatementList node) { return VisitStatementListWorker(node); diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/RuntimeProbing/ModuleCancellationTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/RuntimeProbing/ModuleCancellationTests.cs index 26c01cb91bcdd..6771a4eb4c05c 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/RuntimeProbing/ModuleCancellationTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/RuntimeProbing/ModuleCancellationTests.cs @@ -2783,4 +2783,28 @@ void G(T a, S b) {} var verifier = CompileAndVerify(source); AssertNotInstrumentedWithTokenLoad(verifier, "C.G(T, S, System.Threading.CancellationToken)"); } + + [Fact] + public void FlowPass() + { + var source = """ + using System.Threading; + using System.Collections.Generic; + + class C + { + IEnumerable F() + { + var x = G() as string; + yield return (x != null) ? 1 : 0; + } + + object G(CancellationToken token = default) + => ""; + } + """; + + // definite assignment flow pass doesn't fail + CompileAndVerify(source).VerifyDiagnostics(); + } } diff --git a/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs b/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs index 0c01d91ff3d31..ed8b69df64aa5 100644 --- a/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs +++ b/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs @@ -417,4 +417,51 @@ static ISymbol F(ISymbol s) $" at ")); } + + /// + /// Checks that flow pass handles semantic query code end-to-end + /// (specifically, module cancellation and stack overflow instrumentation). + /// + [ConditionalFact(typeof(CoreClrOnly))] + public async Task FlowPass() + { + using var workspace = TestWorkspace.Create(DefaultWorkspaceXml, composition: FeaturesTestCompositions.Features); + + var solution = workspace.CurrentSolution; + + var service = solution.Services.GetRequiredLanguageService(LanguageNames.CSharp); + + var query = """ + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + + static IEnumerable Find(IMethodSymbol method) + { + var syntaxReference = method.DeclaringSyntaxReferences.FirstOrDefault(); + if (syntaxReference != null) + { + while (true) + { + var syntaxNode = syntaxReference.GetSyntax() as MethodDeclarationSyntax; + if (syntaxNode != null) + { + yield return method; + } + + break; + } + } + } + """; + + var results = new List(); + var observer = new MockSemanticSearchResultsObserver() { OnDefinitionFoundImpl = results.Add }; + var traceSource = new TraceSource("test"); + + var options = workspace.GlobalOptions.GetClassificationOptionsProvider(); + var result = await service.ExecuteQueryAsync(solution, query, s_referenceAssembliesDir, observer, options, traceSource, CancellationToken.None); + + Assert.Null(result.ErrorMessage); + AssertEx.Equal(["void C.VisibleMethod(int)"], results.Select(Inspect)); + } }