diff --git a/ClrHeapAllocationsAnalyzer.Test/ConcatenationAllocationAnalyzerTests.cs b/ClrHeapAllocationsAnalyzer.Test/ConcatenationAllocationAnalyzerTests.cs index d763fc5..10ba080 100644 --- a/ClrHeapAllocationsAnalyzer.Test/ConcatenationAllocationAnalyzerTests.cs +++ b/ClrHeapAllocationsAnalyzer.Test/ConcatenationAllocationAnalyzerTests.cs @@ -7,37 +7,20 @@ namespace ClrHeapAllocationsAnalyzer.Test { [TestClass] - public class ConcatenationAllocationAnalyzerTests : AllocationAnalyzerTests - { + public class ConcatenationAllocationAnalyzerTests : AllocationAnalyzerTests { [TestMethod] - public void ConcatenationAllocation_Basic() - { - var sampleProgram = -@"using System; - -var withBoxing = 5.ToString() + ':' + 8.ToString(); // Boxing on ':' -var withoutBoxing = 5.ToString() + "":"" + 8.ToString(); -"; + public void ConcatenationAllocation_Basic() { + var snippet0 = @"string s0 = ""hello"" + 0.ToString() + ""world"" + 1.ToString();"; + var snippet1 = @"string s2 = ""ohell"" + 2.ToString() + ""world"" + 3.ToString() + 4.ToString();"; var analyser = new ConcatenationAllocationAnalyzer(); - var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(SyntaxKind.AddExpression, SyntaxKind.AddAssignmentExpression)); - - Assert.AreEqual(1, info.Allocations.Count(d => d.Id == ConcatenationAllocationAnalyzer.ValueTypeToReferenceTypeInAStringConcatenationRule.Id)); - Assert.AreEqual(4, info.Allocations.Count(d => d.Id == ConcatenationAllocationAnalyzer.StringConcatenationAllocationRule.Id)); + var info0 = ProcessCode(analyser, snippet0, ImmutableArray.Create(SyntaxKind.AddExpression, SyntaxKind.AddAssignmentExpression)); + var info1 = ProcessCode(analyser, snippet1, ImmutableArray.Create(SyntaxKind.AddExpression, SyntaxKind.AddAssignmentExpression)); - //### CODE ### 5.ToString() + ':' + 8.ToString() - //*** Diagnostic: (9,45): warning HeapAnalyzerBoxingRule: Value type (char) is being boxed to a reference type for a string concatenation. - AssertEx.ContainsDiagnostic(info.Allocations, id: ConcatenationAllocationAnalyzer.ValueTypeToReferenceTypeInAStringConcatenationRule.Id, line: 3, character: 33); - // Diagnostic: (9,43): warning HeapAnalyzerStringConcatRule: Considering using StringBuilder - AssertEx.ContainsDiagnostic(info.Allocations, id: ConcatenationAllocationAnalyzer.StringConcatenationAllocationRule.Id, line: 3, character: 31); - // Diagnostic: (9,49): warning HeapAnalyzerStringConcatRule: Considering using StringBuilder - AssertEx.ContainsDiagnostic(info.Allocations, id: ConcatenationAllocationAnalyzer.StringConcatenationAllocationRule.Id, line: 3, character: 37); + Assert.AreEqual(0, info0.Allocations.Count(d => d.Id == ConcatenationAllocationAnalyzer.StringConcatenationAllocationRule.Id)); + Assert.AreEqual(1, info1.Allocations.Count(d => d.Id == ConcatenationAllocationAnalyzer.StringConcatenationAllocationRule.Id)); - //### CODE ### 5.ToString() + ":" + 8.ToString() - // Diagnostic: (10,46): warning HeapAnalyzerStringConcatRule: Considering using StringBuilder - AssertEx.ContainsDiagnostic(info.Allocations, id: ConcatenationAllocationAnalyzer.StringConcatenationAllocationRule.Id, line: 4, character: 34); - // Diagnostic: (10,52): warning HeapAnalyzerStringConcatRule: Considering using StringBuilder - AssertEx.ContainsDiagnostic(info.Allocations, id: ConcatenationAllocationAnalyzer.StringConcatenationAllocationRule.Id, line: 4, character: 40); + AssertEx.ContainsDiagnostic(info1.Allocations, id: ConcatenationAllocationAnalyzer.StringConcatenationAllocationRule.Id, line: 1, character: 13); } [TestMethod] diff --git a/ClrHeapAllocationsAnalyzer.Test/StackOverflowAnswerTests.cs b/ClrHeapAllocationsAnalyzer.Test/StackOverflowAnswerTests.cs index 169a521..5a396c9 100644 --- a/ClrHeapAllocationsAnalyzer.Test/StackOverflowAnswerTests.cs +++ b/ClrHeapAllocationsAnalyzer.Test/StackOverflowAnswerTests.cs @@ -82,11 +82,9 @@ public void Non_constant_value_types_in_CSharp_string_concatenation() string s1 = ""char value will box"" + c;"; var analyser = new ConcatenationAllocationAnalyzer(); var info = ProcessCode(analyser, @script, ImmutableArray.Create(SyntaxKind.AddExpression, SyntaxKind.AddAssignmentExpression)); - Assert.AreEqual(2, info.Allocations.Count); + Assert.AreEqual(1, info.Allocations.Count); //Diagnostic: (2,53): warning HeapAnalyzerBoxingRule: Value type (char) is being boxed to a reference type for a string concatenation. AssertEx.ContainsDiagnostic(info.Allocations, ConcatenationAllocationAnalyzer.ValueTypeToReferenceTypeInAStringConcatenationRule.Id, line: 2, character: 53); - //Diagnostic: (2,51): warning HeapAnalyzerStringConcatRule: Considering using StringBuilder - AssertEx.ContainsDiagnostic(info.Allocations, ConcatenationAllocationAnalyzer.StringConcatenationAllocationRule.Id, line: 2, character: 51); } [TestMethod] diff --git a/ClrHeapAllocationsAnalyzer/ConcatenationAllocationAnalyzer.cs b/ClrHeapAllocationsAnalyzer/ConcatenationAllocationAnalyzer.cs index 1dd0ba1..4f498f2 100644 --- a/ClrHeapAllocationsAnalyzer/ConcatenationAllocationAnalyzer.cs +++ b/ClrHeapAllocationsAnalyzer/ConcatenationAllocationAnalyzer.cs @@ -33,6 +33,7 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) string filePath = node.SyntaxTree.FilePath; var binaryExpressions = node.DescendantNodesAndSelf().OfType().Reverse(); // need inner most expressions + int stringConcatenationCount = 0; foreach (var binaryExpression in binaryExpressions) { if (binaryExpression.Left == null || binaryExpression.Right == null) @@ -55,10 +56,15 @@ private static void AnalyzeNode(SyntaxNodeAnalysisContext context) // regular string allocation if (left.Type?.SpecialType == SpecialType.System_String || right.Type?.SpecialType == SpecialType.System_String) { - reportDiagnostic(Diagnostic.Create(StringConcatenationAllocationRule, binaryExpression.OperatorToken.GetLocation(), EmptyMessageArgs)); - HeapAllocationAnalyzerEventSource.Logger.StringConcatenationAllocation(filePath); + stringConcatenationCount++; } } + + if (stringConcatenationCount > 3) + { + reportDiagnostic(Diagnostic.Create(StringConcatenationAllocationRule, node.GetLocation(), EmptyMessageArgs)); + HeapAllocationAnalyzerEventSource.Logger.StringConcatenationAllocation(filePath); + } } private static void CheckForTypeConversion(ExpressionSyntax expression, TypeInfo typeInfo, Action reportDiagnostic, string filePath)