diff --git a/analyzers/src/SonarAnalyzer.CFG/LiveVariableAnalysis/RoslynLiveVariableAnalysis.cs b/analyzers/src/SonarAnalyzer.CFG/LiveVariableAnalysis/RoslynLiveVariableAnalysis.cs index 9eeaa5e3789..a4f0aad2691 100644 --- a/analyzers/src/SonarAnalyzer.CFG/LiveVariableAnalysis/RoslynLiveVariableAnalysis.cs +++ b/analyzers/src/SonarAnalyzer.CFG/LiveVariableAnalysis/RoslynLiveVariableAnalysis.cs @@ -24,7 +24,7 @@ namespace SonarAnalyzer.CFG.LiveVariableAnalysis; public sealed class RoslynLiveVariableAnalysis : LiveVariableAnalysisBase { - private readonly Dictionary> flowCaptureOperations = []; + private readonly Dictionary> flowCaptureOperations = []; private readonly Dictionary> blockPredecessors = []; private readonly Dictionary> blockSuccessors = []; @@ -86,24 +86,24 @@ private void ResolveCaptures() .Select(x => x.Instance.ToFlowCapture())) { if (flowCapture.Value.AsFlowCaptureReference() is { } captureReference - && flowCaptureOperations.TryGetValue(captureReference.Id, out var capturedOperations)) + && flowCaptureOperations.TryGetValue(captureReference.Id, out var symbols)) { - AppendFlowCaptureReference(flowCapture.Id, capturedOperations.ToArray()); + AppendFlowCaptureReference(flowCapture.Id, symbols.ToArray()); } - else + else if (ParameterOrLocalSymbol(flowCapture.Value) is { } symbol) { - AppendFlowCaptureReference(flowCapture.Id, flowCapture.Value); + AppendFlowCaptureReference(flowCapture.Id, symbol); } } - void AppendFlowCaptureReference(CaptureId id, params IOperation[] operations) + void AppendFlowCaptureReference(CaptureId id, params ISymbol[] symbols) { if (!flowCaptureOperations.TryGetValue(id, out var list)) { list = []; flowCaptureOperations.Add(id, list); } - list.AddRange(operations); + list.AddRange(symbols); } } @@ -223,17 +223,13 @@ private sealed class RoslynState : State public RoslynState(RoslynLiveVariableAnalysis owner) => this.owner = owner; - public void ProcessBlock(ControlFlowGraph cfg, BasicBlock block, Dictionary> flowCaptureOperations) + public void ProcessBlock(ControlFlowGraph cfg, BasicBlock block, Dictionary> flowCaptureOperations) { foreach (var operation in block.OperationsAndBranchValue.ToReversedExecutionOrder().Select(x => x.Instance)) { - if ((operation.AsFlowCapture() is { } flowCapture && flowCaptureOperations.TryGetValue(flowCapture.Id, out var captured)) - || (operation.AsFlowCaptureReference() is { } flowCaptureReference && flowCaptureOperations.TryGetValue(flowCaptureReference.Id, out captured))) + if (operation.AsFlowCaptureReference() is { } flowCaptureReference && flowCaptureOperations.TryGetValue(flowCaptureReference.Id, out var symbols)) { - foreach (var capturedOperation in captured) - { - ProcessOperation(cfg, capturedOperation); - } + UsedBeforeAssigned.UnionWith(symbols); } else { diff --git a/analyzers/tests/SonarAnalyzer.Test/LiveVariableAnalysis/RoslynLiveVariableAnalysisTest.FlowCaptureOperation.cs b/analyzers/tests/SonarAnalyzer.Test/LiveVariableAnalysis/RoslynLiveVariableAnalysisTest.FlowCaptureOperation.cs index 13393477668..272eed969e3 100644 --- a/analyzers/tests/SonarAnalyzer.Test/LiveVariableAnalysis/RoslynLiveVariableAnalysisTest.FlowCaptureOperation.cs +++ b/analyzers/tests/SonarAnalyzer.Test/LiveVariableAnalysis/RoslynLiveVariableAnalysisTest.FlowCaptureOperation.cs @@ -303,9 +303,9 @@ public void FlowCaptrure_NullCoalescingOperator_Overwrite() var context = CreateContextCS(code, additionalParameters: "string s1"); context.ValidateEntry(LiveIn("s1"), LiveOut("s1")); context.Validate(context.Cfg.Blocks[1], LiveIn("s1")); - context.Validate(context.Cfg.Blocks[2]); // This should have LiveIn("s1") and LiveOut("s1") but #1 gets as value all the assignment operation. - context.Validate(context.Cfg.Blocks[3], LiveOut("s1")); // This should have LiveIn("s1") - context.Validate(context.Cfg.Blocks[4], LiveOut("s1")); // This should have LiveIn("s1") + context.Validate(context.Cfg.Blocks[2], LiveOut("s1")); // This should have LiveIn("s1") and LiveOut("s1") but #1 gets as value all the assignment operation. + context.Validate(context.Cfg.Blocks[3], LiveIn("s1"), LiveOut("s1")); + context.Validate(context.Cfg.Blocks[4], LiveIn("s1"), LiveOut("s1")); context.Validate(context.Cfg.Blocks[5], LiveIn("s1")); context.ValidateExit(); } diff --git a/analyzers/tests/SonarAnalyzer.Test/LiveVariableAnalysis/RoslynLiveVariableAnalysisTest.LocalFunction.cs b/analyzers/tests/SonarAnalyzer.Test/LiveVariableAnalysis/RoslynLiveVariableAnalysisTest.LocalFunction.cs index 8527ff36202..80ee21068bc 100644 --- a/analyzers/tests/SonarAnalyzer.Test/LiveVariableAnalysis/RoslynLiveVariableAnalysisTest.LocalFunction.cs +++ b/analyzers/tests/SonarAnalyzer.Test/LiveVariableAnalysis/RoslynLiveVariableAnalysisTest.LocalFunction.cs @@ -256,8 +256,8 @@ int LocalFunction(int arg) var context = CreateContextCS(code, "LocalFunction"); context.ValidateEntry(LiveIn("arg"), LiveOut("arg")); context.Validate("variable", LiveIn("arg"), LiveOut("arg")); - context.Validate("LocalFunction(arg - 1)", LiveIn("arg"), LiveOut("arg")); - context.Validate("0", LiveIn("arg"), LiveOut("arg")); + context.Validate("LocalFunction(arg - 1)", LiveIn("arg")); + context.Validate("0"); } [TestMethod] diff --git a/analyzers/tests/SonarAnalyzer.Test/TestCases/DeadStores.RoslynCfg.cs b/analyzers/tests/SonarAnalyzer.Test/TestCases/DeadStores.RoslynCfg.cs index f3c6ad1d180..d47d8a46133 100644 --- a/analyzers/tests/SonarAnalyzer.Test/TestCases/DeadStores.RoslynCfg.cs +++ b/analyzers/tests/SonarAnalyzer.Test/TestCases/DeadStores.RoslynCfg.cs @@ -1625,8 +1625,7 @@ public void NestedCatchAndRethrow() // https://github.com/SonarSource/sonar-dotnet/issues/9471 void AssignmentInTernary(bool condition, string st) { - string st2 = condition ? st = "Hi" : "Hello"; // Noncompliant FP - // ^^^^^^^^^ + string st2 = condition ? st = "Hi" : "Hello"; // Compliant Console.WriteLine(st); Console.WriteLine(st2); } @@ -1635,7 +1634,7 @@ void AssignmentInTernary(bool condition, string st) void AssignmentInSwitch() { char ch; - switch (ch = GetAChar()) // Noncompliant FP + switch (ch = GetAChar()) // Compliant { case 'A': break; @@ -1652,9 +1651,9 @@ void AssignmentInSwitch() // https://github.com/SonarSource/sonar-dotnet/issues/9473 void ReassignAfterUsing(IDisposable data) { - using (data = Something()) // Noncompliant + using (data = Something()) // Compliant - if Something() throws, value will be used directly in Console.WriteLine(data); { - data = Something(); // Noncompliant FP + data = Something(); } Console.WriteLine(data);