From c6704f19d95419f2a47ccf830ff6c135b1e8b600 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 17 Feb 2023 11:10:54 +0100 Subject: [PATCH 01/22] Fix unsupported pattern kinds and add tests --- .../Sonar/CSharpControlFlowGraphBuilder.cs | 30 +++ .../Sonar/SonarExplodedGraph.cs | 9 +- .../Sonar/SonarExplodedGraphTest.cs | 244 +++++++++++++++++- 3 files changed, 281 insertions(+), 2 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs b/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs index 47271ec12fb..d4b2acff95c 100644 --- a/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs +++ b/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs @@ -1286,6 +1286,36 @@ private Block BuildPatternExpression(PatternSyntaxWrapper patternSyntaxWrapper, currentBlock.ReversedInstructions.Add(patternSyntaxWrapper); return currentBlock; } + else if (ParenthesizedPatternSyntaxWrapper.IsInstance(patternSyntaxWrapper)) + { + var parenthesized = (ParenthesizedPatternSyntaxWrapper)patternSyntaxWrapper; + return BuildPatternExpression(parenthesized.Pattern, currentBlock); + } + else if (BinaryPatternSyntaxWrapper.IsInstance(patternSyntaxWrapper)) + { + // Unsupported + return currentBlock; + } + else if (UnaryPatternSyntaxWrapper.IsInstance(patternSyntaxWrapper)) + { + // Unsupported + return currentBlock; + } + else if (VarPatternSyntaxWrapper.IsInstance(patternSyntaxWrapper)) + { + // Unsupported + return currentBlock; + } + else if (RelationalPatternSyntaxWrapper.IsInstance(patternSyntaxWrapper)) + { + // Unsupported + return currentBlock; + } + else if (ListPatternSyntaxWrapper.IsInstance(patternSyntaxWrapper)) + { + // Unsupported + return currentBlock; + } throw new NotSupportedException($"{patternSyntaxWrapper.SyntaxNode.Kind()}"); } diff --git a/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs b/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs index 50ff53fd26a..c712c434b7e 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs @@ -515,7 +515,14 @@ protected override void VisitInstruction(ExplodedGraphNode node) } else { - throw new NotSupportedException($"{instruction.Kind()}"); + // Unsupported patten kinds: + // * ParenthesizedPattern + // * BinaryPattern + // * UnaryPattern + // * VarPattern + // * RelationalPattern + // * ListPattern + // * SlicePattern } break; diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index e3bb80b6cb0..09fbd828295 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -28,7 +28,6 @@ using SonarAnalyzer.LiveVariableAnalysis.CSharp; using SonarAnalyzer.SymbolicExecution.Constraints; using SonarAnalyzer.SymbolicExecution.Sonar; -using SonarAnalyzer.SymbolicExecution.Sonar.Constraints; using SonarAnalyzer.UnitTest.CFG.Sonar; using SonarAnalyzer.UnitTest.Helpers; @@ -901,6 +900,249 @@ public void ExplodedGraph_IsPattern_WithPositionalPattern() context.WalkWithInstructions(4); } + [TestMethod] + public void ExplodedGraph_IsPattern_Parentesized() + { + const string testInput = @"var x = obj is (string s);"; + + var context = new ExplodedGraphContext(testInput); + var sSymbol = context.GetSymbol("s", ExplodedGraphContext.SymbolType.Declaration); + + context.ExplodedGraph.InstructionProcessed += (sender, args) => + { + var instruction = args.Instruction.ToString(); + + switch (instruction) + { + case "obj": + args.ProgramState.GetSymbolValue(sSymbol).Should().BeNull(); + break; + + case "obj is (string s)": + args.ProgramState.GetSymbolValue(sSymbol).Should().BeNull(); + args.ProgramState.HasValue.Should().BeTrue(); + break; + + case "x = obj is (string s)": + args.ProgramState.GetSymbolValue(sSymbol).Should().BeNull(); + args.ProgramState.HasValue.Should().BeFalse(); + break; + + default: + throw new NotImplementedException(); + } + }; + + context.WalkWithInstructions(3); + } + + [TestMethod] + public void ExplodedGraph_IsPattern_OrPattern_NotSupported() + { + const string testInput = "var x = obj is 1 or 2;"; + + var context = new ExplodedGraphContext(testInput); + context.ExplodedGraph.InstructionProcessed += (sender, args) => + { + var instruction = args.Instruction.ToString(); + + switch (instruction) + { + case "obj": + case "obj is 1 or 2": + case "x = obj is 1 or 2": + break; + default: + throw new NotImplementedException(); + } + }; + + context.WalkWithInstructions(3); + } + + [TestMethod] + public void ExplodedGraph_IsPattern_AndPattern_NotSupported() + { + const string testInput = "var x = obj is 1 and 2;"; + + var context = new ExplodedGraphContext(testInput); + context.ExplodedGraph.InstructionProcessed += (sender, args) => + { + var instruction = args.Instruction.ToString(); + + switch (instruction) + { + case "obj": + case "obj is 1 and 2": + case "x = obj is 1 and 2": + break; + default: + throw new NotImplementedException(); + } + }; + + context.WalkWithInstructions(3); + } + + [TestMethod] + public void ExplodedGraph_IsPattern_NotPattern_NotSupported() + { + const string testInput = "var x = obj is not 1;"; + + var context = new ExplodedGraphContext(testInput); + context.ExplodedGraph.InstructionProcessed += (sender, args) => + { + var instruction = args.Instruction.ToString(); + + switch (instruction) + { + case "obj": + case "obj is not 1": + case "x = obj is not 1": + break; + default: + throw new NotImplementedException(); + } + }; + + context.WalkWithInstructions(3); + } + + [TestMethod] + public void ExplodedGraph_IsPattern_VarPattern_NotSupported() + { + const string testInput = """ + var x = obj is var i; + i++; + """; + + var context = new ExplodedGraphContext(testInput); + var symbolNotFound = () => context.GetSymbol("o", ExplodedGraphContext.SymbolType.Declaration); + symbolNotFound.Should().Throw("var pattern is not supported"); + + context.ExplodedGraph.InstructionProcessed += (sender, args) => + { + var instruction = args.Instruction.ToString(); + + switch (instruction) + { + case "obj": + case "obj is var i": + case "x = obj is var i": + case "i": + case "i++": + break; + + default: + throw new NotImplementedException(instruction); + } + }; + context.WalkWithInstructions(5); + } + + [TestMethod] + public void ExplodedGraph_IsPattern_RelationalPattern_NotSupported() + { + const string testInput = """ + var x = obj is > 5; + """; + + var context = new ExplodedGraphContext(testInput); + + context.ExplodedGraph.InstructionProcessed += (sender, args) => + { + var instruction = args.Instruction.ToString(); + + switch (instruction) + { + case "obj": + case "obj is > 5": + case "x = obj is > 5": + break; + default: + throw new NotImplementedException(instruction); + } + }; + context.WalkWithInstructions(3); + } + + [TestMethod] + public void ExplodedGraph_IsPattern_ListPattern_NotSupported() + { + const string testInput = """ + var x = new object[] { } is [] empty; + empty.ToString(); + """; + + var context = new ExplodedGraphContext(testInput); + var emptySymbol = context.GetSymbol("empty", ExplodedGraphContext.SymbolType.Declaration); + + context.ExplodedGraph.InstructionProcessed += (sender, args) => + { + var instruction = args.Instruction.ToString(); + + switch (instruction) + { + case "": + args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); + break; + case "object[]": + args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); + break; + case "new object[] { }": + args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); + break; + case "{ }": + args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); + break; + case "new object[] { } is [] empty": + args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); + break; + case "x = new object[] { } is [] empty": + args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); + break; + case "empty": + args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); + break; + case "empty.ToString": + args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); + break; + case "empty.ToString()": + args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); + break; + default: + throw new NotImplementedException(instruction); + } + }; + context.WalkWithInstructions(9); + } + + [TestMethod] + public void ExplodedGraph_SwitchExpression_TypePattern_NotSupported() + { + const string testInput = """ + var x = new object() switch { Exception => true }; + """; + + var context = new ExplodedGraphContext(testInput); + + context.ExplodedGraph.InstructionProcessed += (sender, args) => + { + var instruction = args.Instruction.ToString(); + + switch (instruction) + { + case "Exception": + case "true": + case "x = new object() switch { Exception => true }": + break; + default: + throw new NotImplementedException(instruction); + } + }; + context.WalkWithInstructions(3); + } + [TestMethod] public void ExplodedGraph_SwitchExpressionVisit() { From a0020f2b734a68aac22b5eb81fafc8c05376cc92 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 09:07:13 +0100 Subject: [PATCH 02/22] Remove ifs and throw statement from BuildPatternExpression --- .../Sonar/CSharpControlFlowGraphBuilder.cs | 40 ++++--------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs b/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs index d4b2acff95c..ec24d2801c2 100644 --- a/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs +++ b/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs @@ -1286,38 +1286,14 @@ private Block BuildPatternExpression(PatternSyntaxWrapper patternSyntaxWrapper, currentBlock.ReversedInstructions.Add(patternSyntaxWrapper); return currentBlock; } - else if (ParenthesizedPatternSyntaxWrapper.IsInstance(patternSyntaxWrapper)) - { - var parenthesized = (ParenthesizedPatternSyntaxWrapper)patternSyntaxWrapper; - return BuildPatternExpression(parenthesized.Pattern, currentBlock); - } - else if (BinaryPatternSyntaxWrapper.IsInstance(patternSyntaxWrapper)) - { - // Unsupported - return currentBlock; - } - else if (UnaryPatternSyntaxWrapper.IsInstance(patternSyntaxWrapper)) - { - // Unsupported - return currentBlock; - } - else if (VarPatternSyntaxWrapper.IsInstance(patternSyntaxWrapper)) - { - // Unsupported - return currentBlock; - } - else if (RelationalPatternSyntaxWrapper.IsInstance(patternSyntaxWrapper)) - { - // Unsupported - return currentBlock; - } - else if (ListPatternSyntaxWrapper.IsInstance(patternSyntaxWrapper)) - { - // Unsupported - return currentBlock; - } - - throw new NotSupportedException($"{patternSyntaxWrapper.SyntaxNode.Kind()}"); + // Unsupported: + // Parenthesized + // BinaryPattern + // UnaryPattern + // VarPattern + // RelationalPattern + // ListPattern + return currentBlock; } private Block BuildTupleExpression(TupleExpressionSyntaxWrapper tuple, Block currentBlock) From 93d37caa1e1166576ce998baf7099a2acf603cde Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 09:23:10 +0100 Subject: [PATCH 03/22] Change handling of unsupported pattern to align with exisitng logic --- .../ShimLayer/SyntaxKindEx.cs | 1 + .../Sonar/CSharpControlFlowGraphBuilder.cs | 4 +- .../Sonar/SonarExplodedGraph.cs | 8 ++++ .../CFG/Sonar/SonarControlFlowGraphTest.cs | 40 ------------------- 4 files changed, 12 insertions(+), 41 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CFG/ShimLayer/SyntaxKindEx.cs b/analyzers/src/SonarAnalyzer.CFG/ShimLayer/SyntaxKindEx.cs index fec7e0ca6e9..44316b1e65f 100644 --- a/analyzers/src/SonarAnalyzer.CFG/ShimLayer/SyntaxKindEx.cs +++ b/analyzers/src/SonarAnalyzer.CFG/ShimLayer/SyntaxKindEx.cs @@ -64,6 +64,7 @@ public static class SyntaxKindEx public const SyntaxKind OrPattern = (SyntaxKind)9031; public const SyntaxKind AndPattern = (SyntaxKind)9032; public const SyntaxKind NotPattern = (SyntaxKind)9033; + public const SyntaxKind SlicePattern = (SyntaxKind)9034; public const SyntaxKind ListPattern = (SyntaxKind)9035; public const SyntaxKind DeclarationExpression = (SyntaxKind)9040; public const SyntaxKind RefExpression = (SyntaxKind)9050; diff --git a/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs b/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs index ec24d2801c2..5c095465131 100644 --- a/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs +++ b/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs @@ -1286,6 +1286,7 @@ private Block BuildPatternExpression(PatternSyntaxWrapper patternSyntaxWrapper, currentBlock.ReversedInstructions.Add(patternSyntaxWrapper); return currentBlock; } + // Unsupported: // Parenthesized // BinaryPattern @@ -1293,7 +1294,8 @@ private Block BuildPatternExpression(PatternSyntaxWrapper patternSyntaxWrapper, // VarPattern // RelationalPattern // ListPattern - return currentBlock; + // (SlicePattern) + throw new NotSupportedException($"{patternSyntaxWrapper.SyntaxNode.Kind()}"); } private Block BuildTupleExpression(TupleExpressionSyntaxWrapper tuple, Block currentBlock) diff --git a/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs b/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs index c712c434b7e..2e2ee27f055 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs @@ -523,6 +523,7 @@ protected override void VisitInstruction(ExplodedGraphNode node) // * RelationalPattern // * ListPattern // * SlicePattern + throw new NotSupportedException($"{instruction.Kind()}"); } break; @@ -583,6 +584,13 @@ protected override void VisitInstruction(ExplodedGraphNode node) // Do nothing break; + case SyntaxKindEx.ParenthesizedPattern: + case SyntaxKindEx.ListPattern: + case SyntaxKindEx.SlicePattern: + // Not Supported + // Do nothing + break; + default: throw new NotSupportedException($"{instruction.Kind()}"); } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/CFG/Sonar/SonarControlFlowGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/CFG/Sonar/SonarControlFlowGraphTest.cs index a10a3d0447e..28192084882 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/CFG/Sonar/SonarControlFlowGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/CFG/Sonar/SonarControlFlowGraphTest.cs @@ -4101,46 +4101,6 @@ public void Cfg_SwitchExpression_WhenClause() VerifyAllInstructions(assignment, @"result = first switch {""a"" when second == ""bar"" => 1, ""a"" => 2, ""b"" => 3, _ => 4}"); } - [TestMethod] - public void Cfg_VarPattern_InSwitchExpression_IsNotSupported() - { - var exception = Assert.ThrowsException(() => Build(@"string a = taintedString switch {var x => null};")); - - exception.Message.Should().Be("VarPattern"); - } - - [TestMethod] - public void Cfg_VarPattern_InIf_IsNotSupported() - { - var exception = Assert.ThrowsException(() => Build(@"if (tainted is var x) { }")); - - exception.Message.Should().Be("VarPattern"); - } - - [TestMethod] - public void Cfg_NotPattern_InIf_IsNotSupported() - { - var exception = Assert.ThrowsException(() => Build(@"if (tainted is not null) { }")); - - exception.Message.Should().Be("NotPattern"); - } - - [TestMethod] - public void Cfg_AndPattern_InIf_IsNotSupported() - { - var exception = Assert.ThrowsException(() => Build(@"if (tainted is int and > 0) { }")); - - exception.Message.Should().Be("AndPattern"); - } - - [TestMethod] - public void Cfg_OrPattern_InIf_IsNotSupported() - { - var exception = Assert.ThrowsException(() => Build(@"if (tainted is string or int) { }")); - - exception.Message.Should().Be("OrPattern"); - } - [TestMethod] public void Cfg_Switch_Patterns_NoDefault() { From 270968cb94344bfcb577b641aff5bf1b90105fe1 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 12:38:17 +0100 Subject: [PATCH 04/22] Align test to exisitng implementation --- .../CFG/Sonar/SonarControlFlowGraphTest.cs | 56 ++++ .../Sonar/SonarExplodedGraphTest.cs | 244 +----------------- 2 files changed, 70 insertions(+), 230 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/CFG/Sonar/SonarControlFlowGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/CFG/Sonar/SonarControlFlowGraphTest.cs index 28192084882..10db0c26c67 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/CFG/Sonar/SonarControlFlowGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/CFG/Sonar/SonarControlFlowGraphTest.cs @@ -4101,6 +4101,62 @@ public void Cfg_SwitchExpression_WhenClause() VerifyAllInstructions(assignment, @"result = first switch {""a"" when second == ""bar"" => 1, ""a"" => 2, ""b"" => 3, _ => 4}"); } + [TestMethod] + public void Cfg_VarPattern_InSwitchExpression_IsNotSupported() + { + var exception = Assert.ThrowsException(() => Build(@"string a = taintedString switch {var x => null};")); + + exception.Message.Should().Be("VarPattern"); + } + + [TestMethod] + public void Cfg_VarPattern_InIf_IsNotSupported() + { + var exception = Assert.ThrowsException(() => Build(@"if (tainted is var x) { }")); + + exception.Message.Should().Be("VarPattern"); + } + + [TestMethod] + public void Cfg_NotPattern_InIf_IsNotSupported() + { + var exception = Assert.ThrowsException(() => Build(@"if (tainted is not null) { }")); + + exception.Message.Should().Be("NotPattern"); + } + + [TestMethod] + public void Cfg_AndPattern_InIf_IsNotSupported() + { + var exception = Assert.ThrowsException(() => Build(@"if (tainted is int and > 0) { }")); + + exception.Message.Should().Be("AndPattern"); + } + + [TestMethod] + public void Cfg_OrPattern_InIf_IsNotSupported() + { + var exception = Assert.ThrowsException(() => Build(@"if (tainted is string or int) { }")); + + exception.Message.Should().Be("OrPattern"); + } + + [TestMethod] + public void Cfg_ParenthesizedPattern_InIf_IsNotSupported() + { + var exception = Assert.ThrowsException(() => Build(@"if (tainted is (string s)) { }")); + + exception.Message.Should().Be("ParenthesizedPattern"); + } + + [TestMethod] + public void Cfg_ListPattern_InIf_IsNotSupported() + { + var exception = Assert.ThrowsException(() => Build(@"if (tainted is []) { }")); + + exception.Message.Should().Be("ListPattern"); + } + [TestMethod] public void Cfg_Switch_Patterns_NoDefault() { diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index 09fbd828295..0ff9b4bdd14 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -900,221 +900,17 @@ public void ExplodedGraph_IsPattern_WithPositionalPattern() context.WalkWithInstructions(4); } - [TestMethod] - public void ExplodedGraph_IsPattern_Parentesized() - { - const string testInput = @"var x = obj is (string s);"; - - var context = new ExplodedGraphContext(testInput); - var sSymbol = context.GetSymbol("s", ExplodedGraphContext.SymbolType.Declaration); - - context.ExplodedGraph.InstructionProcessed += (sender, args) => - { - var instruction = args.Instruction.ToString(); - - switch (instruction) - { - case "obj": - args.ProgramState.GetSymbolValue(sSymbol).Should().BeNull(); - break; - - case "obj is (string s)": - args.ProgramState.GetSymbolValue(sSymbol).Should().BeNull(); - args.ProgramState.HasValue.Should().BeTrue(); - break; - - case "x = obj is (string s)": - args.ProgramState.GetSymbolValue(sSymbol).Should().BeNull(); - args.ProgramState.HasValue.Should().BeFalse(); - break; - - default: - throw new NotImplementedException(); - } - }; - - context.WalkWithInstructions(3); - } - - [TestMethod] - public void ExplodedGraph_IsPattern_OrPattern_NotSupported() - { - const string testInput = "var x = obj is 1 or 2;"; - - var context = new ExplodedGraphContext(testInput); - context.ExplodedGraph.InstructionProcessed += (sender, args) => - { - var instruction = args.Instruction.ToString(); - - switch (instruction) - { - case "obj": - case "obj is 1 or 2": - case "x = obj is 1 or 2": - break; - default: - throw new NotImplementedException(); - } - }; - - context.WalkWithInstructions(3); - } - - [TestMethod] - public void ExplodedGraph_IsPattern_AndPattern_NotSupported() - { - const string testInput = "var x = obj is 1 and 2;"; - - var context = new ExplodedGraphContext(testInput); - context.ExplodedGraph.InstructionProcessed += (sender, args) => - { - var instruction = args.Instruction.ToString(); - - switch (instruction) - { - case "obj": - case "obj is 1 and 2": - case "x = obj is 1 and 2": - break; - default: - throw new NotImplementedException(); - } - }; - - context.WalkWithInstructions(3); - } - - [TestMethod] - public void ExplodedGraph_IsPattern_NotPattern_NotSupported() - { - const string testInput = "var x = obj is not 1;"; - - var context = new ExplodedGraphContext(testInput); - context.ExplodedGraph.InstructionProcessed += (sender, args) => - { - var instruction = args.Instruction.ToString(); - - switch (instruction) - { - case "obj": - case "obj is not 1": - case "x = obj is not 1": - break; - default: - throw new NotImplementedException(); - } - }; - - context.WalkWithInstructions(3); - } - - [TestMethod] - public void ExplodedGraph_IsPattern_VarPattern_NotSupported() + [DataTestMethod] + [DataRow("var x = obj is (string s);", "ParenthesizedPattern")] + [DataRow("var x = obj is 1 or 2;", "OrPattern")] + [DataRow("var x = obj is 1 and 2;", "AndPattern")] + [DataRow("var x = obj is not 1;", "NotPattern")] + [DataRow("var x = obj is var i;", "VarPattern")] + [DataRow("var x = obj is > 5;", "RelationalPattern")] + [DataRow("var x = new object[] { } is [] empty;", "ListPattern")] + public void ExplodedGraph_IsPattern_Parentesized(string code, string patternKind) { - const string testInput = """ - var x = obj is var i; - i++; - """; - - var context = new ExplodedGraphContext(testInput); - var symbolNotFound = () => context.GetSymbol("o", ExplodedGraphContext.SymbolType.Declaration); - symbolNotFound.Should().Throw("var pattern is not supported"); - - context.ExplodedGraph.InstructionProcessed += (sender, args) => - { - var instruction = args.Instruction.ToString(); - - switch (instruction) - { - case "obj": - case "obj is var i": - case "x = obj is var i": - case "i": - case "i++": - break; - - default: - throw new NotImplementedException(instruction); - } - }; - context.WalkWithInstructions(5); - } - - [TestMethod] - public void ExplodedGraph_IsPattern_RelationalPattern_NotSupported() - { - const string testInput = """ - var x = obj is > 5; - """; - - var context = new ExplodedGraphContext(testInput); - - context.ExplodedGraph.InstructionProcessed += (sender, args) => - { - var instruction = args.Instruction.ToString(); - - switch (instruction) - { - case "obj": - case "obj is > 5": - case "x = obj is > 5": - break; - default: - throw new NotImplementedException(instruction); - } - }; - context.WalkWithInstructions(3); - } - - [TestMethod] - public void ExplodedGraph_IsPattern_ListPattern_NotSupported() - { - const string testInput = """ - var x = new object[] { } is [] empty; - empty.ToString(); - """; - - var context = new ExplodedGraphContext(testInput); - var emptySymbol = context.GetSymbol("empty", ExplodedGraphContext.SymbolType.Declaration); - - context.ExplodedGraph.InstructionProcessed += (sender, args) => - { - var instruction = args.Instruction.ToString(); - - switch (instruction) - { - case "": - args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); - break; - case "object[]": - args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); - break; - case "new object[] { }": - args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); - break; - case "{ }": - args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); - break; - case "new object[] { } is [] empty": - args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); - break; - case "x = new object[] { } is [] empty": - args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); - break; - case "empty": - args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); - break; - case "empty.ToString": - args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); - break; - case "empty.ToString()": - args.ProgramState.GetSymbolValue(emptySymbol).Should().BeNull(); - break; - default: - throw new NotImplementedException(instruction); - } - }; - context.WalkWithInstructions(9); + Assert.ThrowsException(() => new ExplodedGraphContext(code)).Message.Should().Be(patternKind); } [TestMethod] @@ -1122,25 +918,13 @@ public void ExplodedGraph_SwitchExpression_TypePattern_NotSupported() { const string testInput = """ var x = new object() switch { Exception => true }; + empty.ToString(); """; var context = new ExplodedGraphContext(testInput); - - context.ExplodedGraph.InstructionProcessed += (sender, args) => - { - var instruction = args.Instruction.ToString(); - - switch (instruction) - { - case "Exception": - case "true": - case "x = new object() switch { Exception => true }": - break; - default: - throw new NotImplementedException(instruction); - } - }; - context.WalkWithInstructions(3); + Assert.ThrowsException(() => context.GetSymbol("empty")); + var walk = () => context.WalkWithInstructions(0); + walk.Should().Throw(); } [TestMethod] From 4353d2cec85469f9d81004d4e46c8212601404da Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 12:42:40 +0100 Subject: [PATCH 05/22] Better exception validation --- .../SymbolicExecution/Sonar/SonarExplodedGraphTest.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index 0ff9b4bdd14..c44b768fe98 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -908,10 +908,8 @@ public void ExplodedGraph_IsPattern_WithPositionalPattern() [DataRow("var x = obj is var i;", "VarPattern")] [DataRow("var x = obj is > 5;", "RelationalPattern")] [DataRow("var x = new object[] { } is [] empty;", "ListPattern")] - public void ExplodedGraph_IsPattern_Parentesized(string code, string patternKind) - { + public void ExplodedGraph_IsPattern_Parentesized(string code, string patternKind) => Assert.ThrowsException(() => new ExplodedGraphContext(code)).Message.Should().Be(patternKind); - } [TestMethod] public void ExplodedGraph_SwitchExpression_TypePattern_NotSupported() @@ -924,7 +922,7 @@ public void ExplodedGraph_SwitchExpression_TypePattern_NotSupported() var context = new ExplodedGraphContext(testInput); Assert.ThrowsException(() => context.GetSymbol("empty")); var walk = () => context.WalkWithInstructions(0); - walk.Should().Throw(); + walk.Should().Throw().Which.Message.Should().StartWith("Method Debug.Fail failed"); } [TestMethod] From 512c549d9db14ead13cd17380170df3de31f2c7f Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 13:01:09 +0100 Subject: [PATCH 06/22] Fix test for type pattern --- .../Sonar/SonarExplodedGraphTest.cs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index c44b768fe98..edcb5e5ca14 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -912,17 +912,31 @@ public void ExplodedGraph_IsPattern_WithPositionalPattern() Assert.ThrowsException(() => new ExplodedGraphContext(code)).Message.Should().Be(patternKind); [TestMethod] - public void ExplodedGraph_SwitchExpression_TypePattern_NotSupported() + public void ExplodedGraph_SwitchStatement_TypePattern() { const string testInput = """ - var x = new object() switch { Exception => true }; - empty.ToString(); + switch (new object()) + { + case int: break; + } """; var context = new ExplodedGraphContext(testInput); - Assert.ThrowsException(() => context.GetSymbol("empty")); - var walk = () => context.WalkWithInstructions(0); - walk.Should().Throw().Which.Message.Should().StartWith("Method Debug.Fail failed"); + context.ExplodedGraph.InstructionProcessed += (sender, args) => + { + var instruction = args.Instruction.ToString(); + + switch (instruction) + { + case "new object()": + break; + case "2": + break; + default: + throw new InvalidOperationException($"Unexpected instruction: {instruction}"); + } + }; + context.WalkWithInstructions(2); } [TestMethod] From 718d128acc353fa498359939c6b3ea85cacc5c6a Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 13:01:29 +0100 Subject: [PATCH 07/22] Fix test for type pattern --- .../SymbolicExecution/Sonar/SonarExplodedGraphTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index edcb5e5ca14..b01c02d116e 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -930,7 +930,7 @@ public void ExplodedGraph_SwitchStatement_TypePattern() { case "new object()": break; - case "2": + case "int": break; default: throw new InvalidOperationException($"Unexpected instruction: {instruction}"); From 8a0481077e1451686026e015d94fce8948bec07a Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 13:09:09 +0100 Subject: [PATCH 08/22] Fix test in debug mode --- .../SymbolicExecution/Sonar/SonarExplodedGraphTest.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index b01c02d116e..261a1efd9ff 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -936,7 +936,12 @@ public void ExplodedGraph_SwitchStatement_TypePattern() throw new InvalidOperationException($"Unexpected instruction: {instruction}"); } }; - context.WalkWithInstructions(2); + var walk = () => context.WalkWithInstructions(2); +#if DEBUG + walk.Should().Throw().Which.Message.Should().Be("Expected NumberOfExitBlockReached to be 1, but found 0."); +#else + walk(); +#endif } [TestMethod] From 6345e4f728292afc045347276623783f1f8f34e2 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 13:13:38 +0100 Subject: [PATCH 09/22] Improve ExplodedGraph_IsPattern_Parentesized test --- .../Sonar/SonarExplodedGraphTest.cs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index 261a1efd9ff..05e46739ad1 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -901,15 +901,20 @@ public void ExplodedGraph_IsPattern_WithPositionalPattern() } [DataTestMethod] - [DataRow("var x = obj is (string s);", "ParenthesizedPattern")] - [DataRow("var x = obj is 1 or 2;", "OrPattern")] - [DataRow("var x = obj is 1 and 2;", "AndPattern")] - [DataRow("var x = obj is not 1;", "NotPattern")] - [DataRow("var x = obj is var i;", "VarPattern")] - [DataRow("var x = obj is > 5;", "RelationalPattern")] - [DataRow("var x = new object[] { } is [] empty;", "ListPattern")] - public void ExplodedGraph_IsPattern_Parentesized(string code, string patternKind) => - Assert.ThrowsException(() => new ExplodedGraphContext(code)).Message.Should().Be(patternKind); + [DataRow("is (string s)", "ParenthesizedPattern")] + [DataRow("is 1 or 2", "OrPattern")] + [DataRow("is 1 and 2", "AndPattern")] + [DataRow("is not 1", "NotPattern")] + [DataRow("is var i", "VarPattern")] + [DataRow("is > 5", "RelationalPattern")] + [DataRow("is [] empty", "ListPattern")] + public void ExplodedGraph_IsPattern_Parentesized(string pattern, string patternKind) + { + var createGraph = () => new ExplodedGraphContext($$""" + var x = obj {{pattern}}; + """); + createGraph.Should().ThrowExactly().WithMessage(patternKind); + } [TestMethod] public void ExplodedGraph_SwitchStatement_TypePattern() From eb75766b175fbfdee4de2e42bfab1c8952eaf616 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 14:19:25 +0100 Subject: [PATCH 10/22] Change test name --- .../SymbolicExecution/Sonar/SonarExplodedGraphTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index 05e46739ad1..e8ffdb1fc8f 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -908,7 +908,7 @@ public void ExplodedGraph_IsPattern_WithPositionalPattern() [DataRow("is var i", "VarPattern")] [DataRow("is > 5", "RelationalPattern")] [DataRow("is [] empty", "ListPattern")] - public void ExplodedGraph_IsPattern_Parentesized(string pattern, string patternKind) + public void ExplodedGraph_IsPattern_UnsupportedPatternKinds(string pattern, string patternKind) { var createGraph = () => new ExplodedGraphContext($$""" var x = obj {{pattern}}; From 15735f92388cb8b25bfb9ca7e4e1c8e7af4ad426 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 14:23:04 +0100 Subject: [PATCH 11/22] Fix failing test on Release --- .../SymbolicExecution/Sonar/SonarExplodedGraphTest.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index e8ffdb1fc8f..a47f22c0372 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -942,11 +942,7 @@ public void ExplodedGraph_SwitchStatement_TypePattern() } }; var walk = () => context.WalkWithInstructions(2); -#if DEBUG walk.Should().Throw().Which.Message.Should().Be("Expected NumberOfExitBlockReached to be 1, but found 0."); -#else - walk(); -#endif } [TestMethod] From 9d278e8166793e5dcbb70e0beb2dfd57d39527bd Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 14:27:35 +0100 Subject: [PATCH 12/22] Simplify ExplodedGraph_SwitchStatement_TypePattern test --- .../Sonar/SonarExplodedGraphTest.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index a47f22c0372..8ed332a4feb 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -925,24 +925,15 @@ public void ExplodedGraph_SwitchStatement_TypePattern() case int: break; } """; - var context = new ExplodedGraphContext(testInput); + var actualInstructions = new List(); context.ExplodedGraph.InstructionProcessed += (sender, args) => - { - var instruction = args.Instruction.ToString(); + actualInstructions.Add(args.Instruction.ToString()); - switch (instruction) - { - case "new object()": - break; - case "int": - break; - default: - throw new InvalidOperationException($"Unexpected instruction: {instruction}"); - } - }; var walk = () => context.WalkWithInstructions(2); + walk.Should().Throw().Which.Message.Should().Be("Expected NumberOfExitBlockReached to be 1, but found 0."); + actualInstructions.Should().Equal("new object()", "int"); } [TestMethod] From b455abe5b172eb7740eb4f041eb6512b775f5c30 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 14:43:24 +0100 Subject: [PATCH 13/22] Fix code smell --- .../SymbolicExecution/Sonar/SonarExplodedGraphTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index 8ed332a4feb..22edc95a777 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -932,7 +932,7 @@ public void ExplodedGraph_SwitchStatement_TypePattern() var walk = () => context.WalkWithInstructions(2); - walk.Should().Throw().Which.Message.Should().Be("Expected NumberOfExitBlockReached to be 1, but found 0."); + walk.Should().Throw().WithMessage("Expected NumberOfExitBlockReached to be 1, but found 0."); actualInstructions.Should().Equal("new object()", "int"); } From 774c4cd163b06ba692af7d8f045cc4b8f30b3ff5 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 21 Feb 2023 14:49:06 +0100 Subject: [PATCH 14/22] Add TypePattern as unsupported --- .../src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs b/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs index 5c095465131..6bc281d2362 100644 --- a/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs +++ b/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs @@ -1292,6 +1292,7 @@ private Block BuildPatternExpression(PatternSyntaxWrapper patternSyntaxWrapper, // BinaryPattern // UnaryPattern // VarPattern + // TypePattern // RelationalPattern // ListPattern // (SlicePattern) From 2c1dd92370089e9781a25636634fd57b4c706ae9 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Thu, 23 Feb 2023 16:50:21 +0100 Subject: [PATCH 15/22] Add test in S2259 for parenthesized pattern --- .../Sonar/NullPointerDereference.CSharp11.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Sonar/NullPointerDereference.CSharp11.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Sonar/NullPointerDereference.CSharp11.cs index c6f4e7e4528..a7f515b396b 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Sonar/NullPointerDereference.CSharp11.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Sonar/NullPointerDereference.CSharp11.cs @@ -19,4 +19,17 @@ public void ListPattern(object[] objects) } } } + + // https://github.com/SonarSource/sonar-dotnet/issues/6766 + public class Repo_6766 + { + public void ParenthesizedSwitchPattern(int i) + { + switch (i) + { + case (1 or 2): + break; + } + } + } } From 0d8026ef5611b435c45e4d852bafcb8493dfc528 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Thu, 23 Feb 2023 17:05:28 +0100 Subject: [PATCH 16/22] Add more pattern for switch cases --- .../Sonar/NullPointerDereference.CSharp11.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Sonar/NullPointerDereference.CSharp11.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Sonar/NullPointerDereference.CSharp11.cs index a7f515b396b..9d0bf1f6501 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Sonar/NullPointerDereference.CSharp11.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Sonar/NullPointerDereference.CSharp11.cs @@ -23,11 +23,23 @@ public void ListPattern(object[] objects) // https://github.com/SonarSource/sonar-dotnet/issues/6766 public class Repo_6766 { - public void ParenthesizedSwitchPattern(int i) + public void SwitchCasePattern(object o, object[] array) { - switch (i) + switch (o) { - case (1 or 2): + case (1 or 2): // Parenthesized + case 3 or 4: // Binary + case > 5: // Relational + case char: // Type + case Exception { Message.Length: 1 }: // Recursive + case (int _, int _): // Recursive + case not "": // Unary + break; + } + switch (array) + { + case []: // list pattern + case [1, .., 2]: // list pattern with slice break; } } From 0e21b8fdffb9b93c6c802e79c5783235c18d2c5e Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 24 Feb 2023 13:27:59 +0100 Subject: [PATCH 17/22] Remove commentd --- .../Sonar/CSharpControlFlowGraphBuilder.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs b/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs index 6bc281d2362..47271ec12fb 100644 --- a/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs +++ b/analyzers/src/SonarAnalyzer.CFG/Sonar/CSharpControlFlowGraphBuilder.cs @@ -1287,15 +1287,6 @@ private Block BuildPatternExpression(PatternSyntaxWrapper patternSyntaxWrapper, return currentBlock; } - // Unsupported: - // Parenthesized - // BinaryPattern - // UnaryPattern - // VarPattern - // TypePattern - // RelationalPattern - // ListPattern - // (SlicePattern) throw new NotSupportedException($"{patternSyntaxWrapper.SyntaxNode.Kind()}"); } From 7360dc90c4aa4d239a9f3dc424285d74e3aebd03 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 24 Feb 2023 13:28:27 +0100 Subject: [PATCH 18/22] Remove comments --- .../SymbolicExecution/Sonar/SonarExplodedGraph.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs b/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs index 2e2ee27f055..bccb524269a 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs @@ -515,14 +515,6 @@ protected override void VisitInstruction(ExplodedGraphNode node) } else { - // Unsupported patten kinds: - // * ParenthesizedPattern - // * BinaryPattern - // * UnaryPattern - // * VarPattern - // * RelationalPattern - // * ListPattern - // * SlicePattern throw new NotSupportedException($"{instruction.Kind()}"); } break; From b0a8ade39e8980a5563d891b4eca435b660ac030 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Fri, 24 Feb 2023 13:38:49 +0100 Subject: [PATCH 19/22] Fix test to cover switch cases instead of is pattern --- .../Sonar/SonarExplodedGraphTest.cs | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index 22edc95a777..689d5f4d2b1 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -901,28 +901,19 @@ public void ExplodedGraph_IsPattern_WithPositionalPattern() } [DataTestMethod] - [DataRow("is (string s)", "ParenthesizedPattern")] - [DataRow("is 1 or 2", "OrPattern")] - [DataRow("is 1 and 2", "AndPattern")] - [DataRow("is not 1", "NotPattern")] - [DataRow("is var i", "VarPattern")] - [DataRow("is > 5", "RelationalPattern")] - [DataRow("is [] empty", "ListPattern")] - public void ExplodedGraph_IsPattern_UnsupportedPatternKinds(string pattern, string patternKind) + [DataRow("int")] + [DataRow("(string s)")] + [DataRow("1 or 2")] + [DataRow("1 and 2")] + [DataRow("not 1")] + [DataRow("> 5")] + [DataRow("[] empty")] + public void ExplodedGraph_SwitchStatement_UnsupportedPatternKinds(string pattern) { - var createGraph = () => new ExplodedGraphContext($$""" - var x = obj {{pattern}}; - """); - createGraph.Should().ThrowExactly().WithMessage(patternKind); - } - - [TestMethod] - public void ExplodedGraph_SwitchStatement_TypePattern() - { - const string testInput = """ + var testInput = $$""" switch (new object()) { - case int: break; + case {{pattern}}: break; } """; var context = new ExplodedGraphContext(testInput); @@ -933,7 +924,7 @@ public void ExplodedGraph_SwitchStatement_TypePattern() var walk = () => context.WalkWithInstructions(2); walk.Should().Throw().WithMessage("Expected NumberOfExitBlockReached to be 1, but found 0."); - actualInstructions.Should().Equal("new object()", "int"); + actualInstructions.Should().Equal("new object()", pattern); } [TestMethod] From 8b431764f408b1e46aacd2530984001810c13991 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 28 Feb 2023 14:20:29 +0100 Subject: [PATCH 20/22] Remove throw in SonarExplodedGraph --- .../SymbolicExecution/Sonar/SonarExplodedGraph.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs b/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs index bccb524269a..9a4d7915e99 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs @@ -582,9 +582,6 @@ protected override void VisitInstruction(ExplodedGraphNode node) // Not Supported // Do nothing break; - - default: - throw new NotSupportedException($"{instruction.Kind()}"); } newProgramState = InvokeChecks(newProgramState, (ps, check) => check.PostProcessInstruction(node.ProgramPoint, ps)); From 4c3daa3d511de0e0921aa22418f8483e89d67492 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 28 Feb 2023 14:23:17 +0100 Subject: [PATCH 21/22] Code review --- analyzers/src/SonarAnalyzer.CFG/ShimLayer/SyntaxKindEx.cs | 1 - .../SymbolicExecution/Sonar/SonarExplodedGraphTest.cs | 6 ------ 2 files changed, 7 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CFG/ShimLayer/SyntaxKindEx.cs b/analyzers/src/SonarAnalyzer.CFG/ShimLayer/SyntaxKindEx.cs index 44316b1e65f..fec7e0ca6e9 100644 --- a/analyzers/src/SonarAnalyzer.CFG/ShimLayer/SyntaxKindEx.cs +++ b/analyzers/src/SonarAnalyzer.CFG/ShimLayer/SyntaxKindEx.cs @@ -64,7 +64,6 @@ public static class SyntaxKindEx public const SyntaxKind OrPattern = (SyntaxKind)9031; public const SyntaxKind AndPattern = (SyntaxKind)9032; public const SyntaxKind NotPattern = (SyntaxKind)9033; - public const SyntaxKind SlicePattern = (SyntaxKind)9034; public const SyntaxKind ListPattern = (SyntaxKind)9035; public const SyntaxKind DeclarationExpression = (SyntaxKind)9040; public const SyntaxKind RefExpression = (SyntaxKind)9050; diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs index 689d5f4d2b1..4ab0ad9c7a6 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Sonar/SonarExplodedGraphTest.cs @@ -917,14 +917,8 @@ public void ExplodedGraph_SwitchStatement_UnsupportedPatternKinds(string pattern } """; var context = new ExplodedGraphContext(testInput); - var actualInstructions = new List(); - context.ExplodedGraph.InstructionProcessed += (sender, args) => - actualInstructions.Add(args.Instruction.ToString()); - var walk = () => context.WalkWithInstructions(2); - walk.Should().Throw().WithMessage("Expected NumberOfExitBlockReached to be 1, but found 0."); - actualInstructions.Should().Equal("new object()", pattern); } [TestMethod] From 1f58b1a05cbec2acea07e0e7aa3390c5dadd4849 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Tue, 28 Feb 2023 14:24:40 +0100 Subject: [PATCH 22/22] Remove all pattern cases and just do default: nothing. --- .../SymbolicExecution/Sonar/SonarExplodedGraph.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs b/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs index 9a4d7915e99..d677065704e 100644 --- a/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs +++ b/analyzers/src/SonarAnalyzer.CSharp/SymbolicExecution/Sonar/SonarExplodedGraph.cs @@ -576,9 +576,7 @@ protected override void VisitInstruction(ExplodedGraphNode node) // Do nothing break; - case SyntaxKindEx.ParenthesizedPattern: - case SyntaxKindEx.ListPattern: - case SyntaxKindEx.SlicePattern: + default: // Not Supported // Do nothing break;