public void TypeConversionAllocation_ArgumentSyntax()
        {
            var sampleProgram =
            @"using System;

            var result = fooObjCall(10); // Allocation
            var temp = new MyObject(10); // Allocation

            private string fooObjCall(object obj)
            {
            return obj.ToString();
            }

            public class MyObject
            {
            private Object Obj;

            public MyObject(object obj)
            {
            this.Obj = obj;
            }
            }";

            var analyser = new TypeConversionAllocationAnalyzer();
            var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(SyntaxKind.Argument));

            Assert.AreEqual(2, info.Allocations.Count);
            // Diagnostic: (3,25): warning HeapAnalyzerBoxingRule: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable ***
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule.Id, line: 3, character: 25);
            // Diagnostic: (4,25): warning HeapAnalyzerBoxingRule: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable ***
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule.Id, line: 4, character: 25);
        }
 public void Converting_any_enumeration_type_to_System_Enum_type()
 {
     var @script =
         @"enum E { A }
         System.Enum box = E.A;";
     var analyser = new TypeConversionAllocationAnalyzer();
     var info = ProcessCode(analyser, @script, ImmutableArray.Create(
         SyntaxKind.SimpleAssignmentExpression,
         SyntaxKind.ReturnStatement,
         SyntaxKind.YieldReturnStatement,
         SyntaxKind.CastExpression,
         SyntaxKind.AsExpression,
         SyntaxKind.CoalesceExpression,
         SyntaxKind.ConditionalExpression,
         SyntaxKind.ForEachStatement,
         SyntaxKind.EqualsValueClause,
         SyntaxKind.Argument));
     Assert.AreEqual(1, info.Allocations.Count);
     // Diagnostic: (2,35): warning HeapAnalyzerBoxingRule: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable
     AssertEx.ContainsDiagnostic(info.Allocations, TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule.Id, line: 2, character: 35);
 }
        public void TypeConversionAllocation_ArgumentSyntax_WithDelegates()
        {
            var sampleProgram =
            @"using System;

            public class MyClass
            {
            public void Testing()
            {
            var @class = new MyClass();
            @class.ProcessFunc(fooObjCall); // implicit, so Allocation
            @class.ProcessFunc(new Func<object, string>(fooObjCall)); // Explicit, so NO Allocation
            }

            public void ProcessFunc(Func<object, string> func)
            {
            }

            private string fooObjCall(object obj)
            {
            return obj.ToString();
            }
            }

            public struct MyStruct
            {
            public void Testing()
            {
            var @struct = new MyStruct();
            @struct.ProcessFunc(fooObjCall); // implicit, so Allocation
            @struct.ProcessFunc(new Func<object, string>(fooObjCall)); // Explicit, so NO Allocation
            }

            public void ProcessFunc(Func<object, string> func)
            {
            }

            private string fooObjCall(object obj)
            {
            return obj.ToString();
            }
            }";

            var analyser = new TypeConversionAllocationAnalyzer();
            var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(SyntaxKind.Argument));

            Assert.AreEqual(3, info.Allocations.Count);
            // Diagnostic: (8,28): warning HeapAnalyzerMethodGroupAllocationRule: This will allocate a delegate instance
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.MethodGroupAllocationRule.Id, line: 8, character: 28);
            // Diagnostic: (27,29): warning HeapAnalyzerMethodGroupAllocationRule: This will allocate a delegate instance
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.MethodGroupAllocationRule.Id, line: 27, character: 29);
            // Diagnostic: (27,29): warning HeapAnalyzerDelegateOnStructRule: Struct instance method being used for delegate creation, this will result in a boxing instruction
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule.Id, line: 27, character: 29);
        }
        public void TypeConversionAllocation_YieldStatementSyntax()
        {
            var sampleProgram =
            @"using System;
            using System.Collections.Generic;

            foreach (var item in GetItems())
            {
            }

            foreach (var item in GetItemsNoAllocation())
            {
            }

            public IEnumerable<object> GetItems()
            {
            yield return 0; // Allocation
            yield break;
            }

            public IEnumerable<int> GetItemsNoAllocation()
            {
            yield return 0; // NO Allocation (IEnumerable<int>)
            yield break;
            }";

            var analyser = new TypeConversionAllocationAnalyzer();
            var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(SyntaxKind.YieldReturnStatement));

            Assert.AreEqual(1, info.Allocations.Count);

            // Diagnostic: (14,18): warning HeapAnalyzerBoxingRule: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule.Id, line: 14, character: 18);
            // TODO this is a false positive
            // Diagnostic: (8,22): warning HeapAnalyzerBoxingRule: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable
        }
        public void TypeConversionAllocation_ReturnStatementSyntax()
        {
            var sampleProgram =
            @"using System;

            var result1 = new MyObject().Obj; // Allocation
            var result2 = new MyObject().ObjNoAllocation; // Allocation

            public class MyObject
            {
            public Object Obj { get { return 0; } }

            public Object ObjNoAllocation { get { return 0.ToString(); } }
            }";

            var analyser = new TypeConversionAllocationAnalyzer();
            var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(SyntaxKind.ReturnStatement));

            Assert.AreEqual(1, info.Allocations.Count);

            // Diagnostic: (7,38): warning HeapAnalyzerBoxingRule: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule.Id, line: 8, character: 38);
        }
        public void TypeConversionAllocation_ImplicitStringCastOperator()
        {
            var sampleProgram = @"
                public struct AStruct
                {
                    public readonly string WrappedString;

                    public AStruct(string s)
                    {
                        WrappedString = s ?? """";
                    }

                    public static void Dump(AStruct astruct)
                    {
                        System.Console.WriteLine(astruct);
                    }

                    public static implicit operator string(AStruct astruct)
                    {
                        return astruct.WrappedString;
                    }
                }
                public class Program
                {
                    public static void Main()
                    {
                        var astruct = new AStruct(System.Environment.MachineName);
                        AStruct.Dump(astruct);
                    }
                }
            ";
            var analyzer = new TypeConversionAllocationAnalyzer();
            var info = ProcessCode(analyzer, sampleProgram, ImmutableArray.Create(SyntaxKind.Argument));
            Assert.AreEqual(0, info.Allocations.Count);
            // currently info.Allocations.Count == 1
            // with info.Allocations[0] =
            // (13,50): warning HeapAnalyzerBoxingRule: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable
        }
        public void TypeConversionAllocation_EqualsValueClause_ExplicitMethodGroupAllocation_Bug()
        {
            // See https://github.com/mjsabby/RoslynClrHeapAllocationAnalyzer/issues/2
            var sampleProgram =
            @"using System;

            public class MyClass
            {
            public void Testing()
            {
            Action methodGroup = this.Method;
            }

            private void Method()
            {
            }
            }

            public struct MyStruct
            {
            public void Testing()
            {
            Action methodGroup = this.Method;
            }

            private void Method()
            {
            }
            }";

            var analyser = new TypeConversionAllocationAnalyzer();
            var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(SyntaxKind.EqualsValueClause));

            Assert.AreEqual(3, info.Allocations.Count);
        }
        public void TypeConversionAllocation_EqualsValueClauseSyntax_WithDelegates()
        {
            var sampleProgram =
            @"using System;

            public class MyClass
            {
            public void Testing()
            {
            Func<object, string> func2 = fooObjCall; // implicit, so Allocation
            Func<object, string> func1 = new Func<object, string>(fooObjCall); // Explicit, so NO Allocation
            }

            private string fooObjCall(object obj)
            {
            return obj.ToString();
            }
            }

            public struct MyStruct
            {
            public void Testing()
            {
            Func<object, string> func2 = fooObjCall; // implicit, so Allocation
            Func<object, string> func1 = new Func<object, string>(fooObjCall); // Explicit, so NO Allocation
            }

            private string fooObjCall(object obj)
            {
            return obj.ToString();
            }
            }";

            var analyser = new TypeConversionAllocationAnalyzer();
            var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(SyntaxKind.CoalesceExpression, SyntaxKind.EqualsValueClause));

            Assert.AreEqual(3, info.Allocations.Count);

            // Diagnostic: (7,38): warning HeapAnalyzerMethodGroupAllocationRule: This will allocate a delegate instance
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.MethodGroupAllocationRule.Id, line: 7, character: 38);
            // Diagnostic: (21,38): warning HeapAnalyzerMethodGroupAllocationRule: This will allocate a delegate instance
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.MethodGroupAllocationRule.Id, line: 21, character: 38);
            // Diagnostic: (21,38): warning HeapAnalyzerDelegateOnStructRule: Struct instance method being used for delegate creation, this will result in a boxing instruction
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule.Id, line: 21, character: 38);
            // TODO this is a false positive
            // Diagnostic: (22,63): warning HeapAnalyzerDelegateOnStructRule: Struct instance method being used for delegate creation, this will result in a boxing instruction
        }
        public void TypeConversionAllocation_EqualsValueClauseSyntax()
        {
            // for (object i = 0;;)
            var sampleProgram =
            @"using System;

            for (object i = 0;;) // Allocation
            {
            }

            for (int i = 0;;) // NO Allocation
            {
            }";

            var analyser = new TypeConversionAllocationAnalyzer();
            var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(
                SyntaxKind.SimpleAssignmentExpression,
                SyntaxKind.ReturnStatement,
                SyntaxKind.YieldReturnStatement,
                SyntaxKind.CastExpression,
                SyntaxKind.AsExpression,
                SyntaxKind.CoalesceExpression,
                SyntaxKind.ConditionalExpression,
                SyntaxKind.ForEachStatement,
                SyntaxKind.EqualsValueClause,
                SyntaxKind.Argument));

            Assert.AreEqual(1, info.Allocations.Count);

            // Diagnostic: (3,17): warning HeapAnalyzerBoxingRule: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule.Id, line: 3, character: 17);
        }
        public void TypeConversionAllocation_ConditionalExpressionSyntax()
        {
            var sampleProgram =
            @"using System;

            object obj = ""test"";
            object test1 = true ? 0 : obj; // Allocation
            object test2 = true ? 0.ToString() : obj; // NO Allocation
            ";

            var analyser = new TypeConversionAllocationAnalyzer();
            var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(SyntaxKind.ConditionalExpression));

            Assert.AreEqual(1, info.Allocations.Count);

            // Diagnostic: (4,23): warning HeapAnalyzerBoxingRule: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule.Id, line: 4, character: 23);
        }
        public void TypeConversionAllocation_CastExpressionSyntax()
        {
            var sampleProgram =
            @"using System;

            var f1 = (object)5; // Allocation
            var f2 = (object)""5""; // NO Allocation
            ";

            var analyser = new TypeConversionAllocationAnalyzer();
            var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(SyntaxKind.CastExpression));

            Assert.AreEqual(1, info.Allocations.Count);

            // Diagnostic: (3,18): warning HeapAnalyzerBoxingRule: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule.Id, line: 3, character: 18);
        }
        public void TypeConversionAllocation_BinaryExpressionSyntax_WithDelegates()
        {
            var sampleProgram =
            @"using System;

            public class MyClass
            {
            public void Testing()
            {
            Func<object, string> temp = null;
            var result1 = temp ?? fooObjCall; // implicit, so Allocation
            var result2 = temp ?? new Func<object, string>(fooObjCall); // Explicit, so NO Allocation
            }

            private string fooObjCall(object obj)
            {
            return obj.ToString();
            }
            }

            public struct MyStruct
            {
            public void Testing()
            {
            Func<object, string> temp = null;
            var result1 = temp ?? fooObjCall; // implicit, so Allocation
            var result2 = temp ?? new Func<object, string>(fooObjCall); // Explicit, so NO Allocation
            }

            private string fooObjCall(object obj)
            {
            return obj.ToString();
            }
            }";

            var analyser = new TypeConversionAllocationAnalyzer();
            var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(SyntaxKind.CoalesceExpression, SyntaxKind.AsExpression));

            Assert.AreEqual(3, info.Allocations.Count);

            // Diagnostic: (8,31): warning HeapAnalyzerMethodGroupAllocationRule: This will allocate a delegate instance
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.MethodGroupAllocationRule.Id, line: 8, character: 31);
            // Diagnostic: (23,31): warning HeapAnalyzerMethodGroupAllocationRule: This will allocate a delegate instance
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.MethodGroupAllocationRule.Id, line: 23, character: 31);
            // Diagnostic: (23,31): warning HeapAnalyzerDelegateOnStructRule: Struct instance method being used for delegate creation, this will result in a boxing instruction
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule.Id, line: 23, character: 31);
        }
        public void TypeConversionAllocation_BinaryExpressionSyntax()
        {
            var sampleProgram =
            @"using System;

            object x = ""blah"";
            object a1 = x ?? 0; // Allocation
            object a2 = x ?? 0.ToString(); // No Allocation

            var b1 = 10 as object; // Allocation
            var b2 = 10.ToString() as object; // No Allocation
            ";

            var analyser = new TypeConversionAllocationAnalyzer();
            var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(SyntaxKind.CoalesceExpression, SyntaxKind.AsExpression));

            Assert.AreEqual(2, info.Allocations.Count);

            // Diagnostic: (4,17): warning HeapAnalyzerBoxingRule: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule.Id, line: 4, character: 18);
            // Diagnostic: (7,9): warning HeapAnalyzerBoxingRule: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable
            AssertEx.ContainsDiagnostic(info.Allocations, id: TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule.Id, line: 7, character: 10);
        }
 public void Creating_delegate_from_value_type_instance_method()
 {
     var @script =
         @"using System;
         struct S { public void M() {} }
         Action box = new S().M;";
     var analyser = new TypeConversionAllocationAnalyzer();
     var info = ProcessCode(analyser, @script, ImmutableArray.Create(
         SyntaxKind.SimpleAssignmentExpression,
         SyntaxKind.ReturnStatement,
         SyntaxKind.YieldReturnStatement,
         SyntaxKind.CastExpression,
         SyntaxKind.AsExpression,
         SyntaxKind.CoalesceExpression,
         SyntaxKind.ConditionalExpression,
         SyntaxKind.ForEachStatement,
         SyntaxKind.EqualsValueClause,
         SyntaxKind.Argument));
     Assert.AreEqual(2, info.Allocations.Count);
     // Diagnostic: (3,30): warning HeapAnalyzerMethodGroupAllocationRule: This will allocate a delegate instance
     AssertEx.ContainsDiagnostic(info.Allocations, TypeConversionAllocationAnalyzer.MethodGroupAllocationRule.Id, line: 3, character: 30);
     // Diagnostic: (3,30): warning HeapAnalyzerDelegateOnStructRule: Struct instance method being used for delegate creation, this will result in a boxing instruction
     AssertEx.ContainsDiagnostic(info.Allocations, TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule.Id, line: 3, character: 30);
 }