diff --git a/src/DendroDocs.Tool/Analyzers/InvocationsAnalyzer.cs b/src/DendroDocs.Tool/Analyzers/InvocationsAnalyzer.cs
index 49123a6..1fac204 100644
--- a/src/DendroDocs.Tool/Analyzers/InvocationsAnalyzer.cs
+++ b/src/DendroDocs.Tool/Analyzers/InvocationsAnalyzer.cs
@@ -30,6 +30,37 @@ public override void VisitObjectCreationExpression(ObjectCreationExpressionSynta
base.VisitObjectCreationExpression(node);
}
+ public override void VisitImplicitObjectCreationExpression(ImplicitObjectCreationExpressionSyntax node)
+ {
+ // For implicit object creation (new()), get the type from the semantic model
+ var typeInfo = semanticModel.GetTypeInfo(node);
+ string containingType = typeInfo.Type?.ToDisplayString() ?? string.Empty;
+ string typeName = typeInfo.Type?.Name ?? "object";
+
+ var invocation = new InvocationDescription(containingType, typeName);
+ statements.Add(invocation);
+
+ if (node.ArgumentList != null)
+ {
+ foreach (var argument in node.ArgumentList.Arguments)
+ {
+ var argumentDescription = new ArgumentDescription(semanticModel.GetTypeDisplayString(argument.Expression), argument.Expression.ToString());
+ invocation.Arguments.Add(argumentDescription);
+ }
+ }
+
+ if (node.Initializer != null)
+ {
+ foreach (var expression in node.Initializer.Expressions)
+ {
+ var argumentDescription = new ArgumentDescription(semanticModel.GetTypeDisplayString(expression), expression.ToString());
+ invocation.Arguments.Add(argumentDescription);
+ }
+ }
+
+ base.VisitImplicitObjectCreationExpression(node);
+ }
+
public override void VisitSwitchStatement(SwitchStatementSyntax node)
{
var branchingAnalyzer = new BranchingAnalyzer(semanticModel, statements);
diff --git a/src/DendroDocs.Tool/Analyzers/OperationBasedInvocationsAnalyzer.cs b/src/DendroDocs.Tool/Analyzers/OperationBasedInvocationsAnalyzer.cs
new file mode 100644
index 0000000..180e822
--- /dev/null
+++ b/src/DendroDocs.Tool/Analyzers/OperationBasedInvocationsAnalyzer.cs
@@ -0,0 +1,135 @@
+using Microsoft.CodeAnalysis.Operations;
+
+namespace DendroDocs.Tool;
+
+///
+/// OperationWalker-based analyzer for method invocations, providing better support for VB.NET,
+/// constant values, and implicit object creation expressions.
+///
+/// Benefits over CSharpSyntaxWalker:
+/// - Language-agnostic: Works with both C# and VB.NET
+/// - Better type information: Access to resolved types and constant values
+/// - Unified object creation: Handles both explicit and implicit object creation seamlessly
+/// - Enhanced semantic analysis: Works at the operation level rather than syntax level
+///
+/// Example usage for VB.NET support:
+/// Instead of needing separate VB-specific syntax walkers, this analyzer can process
+/// VB.NET code through IOperation, making it language-neutral.
+///
+internal class OperationBasedInvocationsAnalyzer(SemanticModel semanticModel, List statements) : OperationWalker
+{
+ // Keep semantic model available for future enhancements
+ private readonly SemanticModel _semanticModel = semanticModel;
+ public override void VisitObjectCreation(IObjectCreationOperation operation)
+ {
+ string containingType = operation.Type?.ToDisplayString() ?? string.Empty;
+ string typeName = operation.Type?.Name ?? string.Empty;
+
+ var invocation = new InvocationDescription(containingType, typeName);
+ statements.Add(invocation);
+
+ foreach (var argument in operation.Arguments)
+ {
+ var value = GetConstantValueOrDefault(argument.Value);
+ var argumentDescription = new ArgumentDescription(argument.Value.Type?.ToDisplayString() ?? string.Empty, value);
+ invocation.Arguments.Add(argumentDescription);
+ }
+
+ if (operation.Initializer != null)
+ {
+ foreach (var initializer in operation.Initializer.Initializers)
+ {
+ var value = initializer switch
+ {
+ IAssignmentOperation assignment => assignment.Value.Syntax.ToString(),
+ _ => initializer.Syntax.ToString()
+ };
+
+ var argumentDescription = new ArgumentDescription(initializer.Type?.ToDisplayString() ?? string.Empty, value);
+ invocation.Arguments.Add(argumentDescription);
+ }
+ }
+
+ base.VisitObjectCreation(operation);
+ }
+
+ public override void VisitInvocation(IInvocationOperation operation)
+ {
+ // Check for nameof expression
+ if (operation.TargetMethod.Name == "nameof" && operation.Arguments.Length == 1)
+ {
+ // nameof is compiler sugar, and is actually a method we are not interested in
+ return;
+ }
+
+ var containingType = operation.TargetMethod.ContainingType?.ToDisplayString() ?? string.Empty;
+ var methodName = operation.TargetMethod.Name;
+
+ var invocation = new InvocationDescription(containingType, methodName);
+ statements.Add(invocation);
+
+ foreach (var argument in operation.Arguments)
+ {
+ var value = GetConstantValueOrDefault(argument.Value);
+ var argumentDescription = new ArgumentDescription(argument.Value.Type?.ToDisplayString() ?? string.Empty, value);
+ invocation.Arguments.Add(argumentDescription);
+ }
+
+ base.VisitInvocation(operation);
+ }
+
+ public override void VisitReturn(IReturnOperation operation)
+ {
+ var value = operation.ReturnedValue != null ? GetConstantValueOrDefault(operation.ReturnedValue) : string.Empty;
+ var returnDescription = new ReturnDescription(value);
+ statements.Add(returnDescription);
+
+ base.VisitReturn(operation);
+ }
+
+ public override void VisitSimpleAssignment(ISimpleAssignmentOperation operation)
+ {
+ var target = operation.Target.Syntax.ToString();
+ var value = operation.Value.Syntax.ToString();
+
+ var assignmentDescription = new AssignmentDescription(target, "=", value);
+ statements.Add(assignmentDescription);
+
+ base.VisitSimpleAssignment(operation);
+ }
+
+ public override void VisitCompoundAssignment(ICompoundAssignmentOperation operation)
+ {
+ var target = operation.Target.Syntax.ToString();
+ var value = operation.Value.Syntax.ToString();
+ var operatorToken = operation.OperatorKind switch
+ {
+ BinaryOperatorKind.Add => "+=",
+ BinaryOperatorKind.Subtract => "-=",
+ BinaryOperatorKind.Multiply => "*=",
+ BinaryOperatorKind.Divide => "/=",
+ BinaryOperatorKind.Remainder => "%=",
+ BinaryOperatorKind.And => "&=",
+ BinaryOperatorKind.Or => "|=",
+ BinaryOperatorKind.ExclusiveOr => "^=",
+ BinaryOperatorKind.LeftShift => "<<=",
+ BinaryOperatorKind.RightShift => ">>=",
+ _ => "="
+ };
+
+ var assignmentDescription = new AssignmentDescription(target, operatorToken, value);
+ statements.Add(assignmentDescription);
+
+ base.VisitCompoundAssignment(operation);
+ }
+
+ private static string GetConstantValueOrDefault(IOperation operation)
+ {
+ return operation switch
+ {
+ ILiteralOperation literal => literal.ConstantValue.Value?.ToString() ?? string.Empty,
+ IFieldReferenceOperation field when field.Field.IsConst => field.Field.ConstantValue?.ToString() ?? string.Empty,
+ _ => operation.Syntax.ToString()
+ };
+ }
+}
\ No newline at end of file
diff --git a/tests/DendroDocs.Tool.Tests/ImplicitObjectCreationTests.cs b/tests/DendroDocs.Tool.Tests/ImplicitObjectCreationTests.cs
new file mode 100644
index 0000000..7c61bd7
--- /dev/null
+++ b/tests/DendroDocs.Tool.Tests/ImplicitObjectCreationTests.cs
@@ -0,0 +1,323 @@
+namespace DendroDocs.Tool.Tests;
+
+[TestClass]
+public class ImplicitObjectCreationTests
+{
+ [TestMethod]
+ public void ImplicitObjectCreation_Should_BeDetected()
+ {
+ // Assign
+ var source = @"
+ class Test
+ {
+ void Method()
+ {
+ object obj = new();
+ }
+ }
+ ";
+
+ // Act
+ var types = TestHelper.VisitSyntaxTree(source);
+
+ // Assert
+ var method = types[0].Methods[0];
+ method.Statements.Count.ShouldBe(1);
+ method.Statements[0].ShouldBeOfType();
+ }
+
+ [TestMethod]
+ public void ExplicitObjectCreation_Should_StillWork()
+ {
+ // Assign
+ var source = @"
+ class Test
+ {
+ void Method()
+ {
+ object obj = new object();
+ }
+ }
+ ";
+
+ // Act
+ var types = TestHelper.VisitSyntaxTree(source);
+
+ // Assert
+ var method = types[0].Methods[0];
+ method.Statements.Count.ShouldBe(1);
+ method.Statements[0].ShouldBeOfType();
+ }
+
+ [TestMethod]
+ public void ImplicitObjectCreation_WithArguments_Should_BeDetected()
+ {
+ // Assign
+ var source = @"
+ class Test
+ {
+ void Method()
+ {
+ System.Text.StringBuilder sb = new(""Hello"");
+ }
+ }
+ ";
+
+ // Act
+ var types = TestHelper.VisitSyntaxTree(source);
+
+ // Assert
+ var method = types[0].Methods[0];
+ method.Statements.Count.ShouldBe(1);
+ method.Statements[0].ShouldBeOfType();
+
+ var invocation = (InvocationDescription)method.Statements[0];
+ invocation.Arguments.Count.ShouldBe(1);
+ }
+
+ [TestMethod]
+ public void ImplicitObjectCreation_WithMultipleArguments_Should_BeDetected()
+ {
+ // Assign
+ var source = @"
+ class Test
+ {
+ void Method()
+ {
+ System.Text.StringBuilder sb = new(""Hello"", 100);
+ }
+ }
+ ";
+
+ // Act
+ var types = TestHelper.VisitSyntaxTree(source);
+
+ // Assert
+ var method = types[0].Methods[0];
+ method.Statements.Count.ShouldBe(1);
+ method.Statements[0].ShouldBeOfType();
+
+ var invocation = (InvocationDescription)method.Statements[0];
+ invocation.Arguments.Count.ShouldBe(2);
+ }
+
+ [TestMethod]
+ public void ImplicitObjectCreation_WithInitializers_Should_BeDetected()
+ {
+ // Assign
+ var source = @"
+ public class Person
+ {
+ public string Name { get; set; }
+ public int Age { get; set; }
+ }
+
+ class Test
+ {
+ void Method()
+ {
+ Person person = new() { Name = ""John"", Age = 30 };
+ }
+ }
+ ";
+
+ // Act
+ var types = TestHelper.VisitSyntaxTree(source);
+
+ // Assert
+ var method = types[1].Methods[0]; // Second type is Test class
+
+ // Should detect object creation
+ method.Statements.Count.ShouldBeGreaterThan(0);
+
+ var invocations = method.Statements.OfType().ToList();
+ invocations.Count.ShouldBeGreaterThan(0);
+
+ // Should have arguments for the initializers
+ invocations[0].Arguments.Count.ShouldBeGreaterThanOrEqualTo(0);
+ }
+
+ [TestMethod]
+ public void ImplicitObjectCreation_WithArgumentsAndInitializers_Should_BeDetected()
+ {
+ // Assign
+ var source = @"
+ public class CustomList
+ {
+ public CustomList(int capacity) { }
+ public string Name { get; set; }
+ public int Count { get; set; }
+ }
+
+ class Test
+ {
+ void Method()
+ {
+ CustomList list = new(10) { Name = ""MyList"", Count = 5 };
+ }
+ }
+ ";
+
+ // Act
+ var types = TestHelper.VisitSyntaxTree(source);
+
+ // Assert
+ var method = types[1].Methods[0]; // Second type is Test class
+
+ // Should detect object creation
+ method.Statements.Count.ShouldBeGreaterThan(0);
+
+ var invocations = method.Statements.OfType().ToList();
+ invocations.Count.ShouldBeGreaterThan(0);
+
+ // Should have arguments for constructor and initializers
+ invocations[0].Arguments.Count.ShouldBeGreaterThanOrEqualTo(1);
+ }
+
+ [TestMethod]
+ public void ImplicitObjectCreation_InVariousContexts_Should_BeDetected()
+ {
+ // Assign
+ var source = @"
+ class Test
+ {
+ void Method()
+ {
+ // Local variable
+ var list1 = new System.Collections.Generic.List();
+
+ // Assignment
+ System.Collections.Generic.List list2;
+ list2 = new();
+
+ // Method parameter
+ ProcessList(new());
+
+ // Return value
+ var result = CreateList();
+ }
+
+ void ProcessList(System.Collections.Generic.List list) { }
+
+ System.Collections.Generic.List CreateList()
+ {
+ return new();
+ }
+ }
+ ";
+
+ // Act
+ var types = TestHelper.VisitSyntaxTree(source);
+
+ // Assert
+ var method = types[0].Methods[0];
+ var processMethod = types[0].Methods[1];
+ var createMethod = types[0].Methods[2];
+
+ // Method should have object creations and method calls
+ var invocations = method.Statements.OfType().ToList();
+ invocations.Count.ShouldBeGreaterThan(0);
+
+ // CreateList method should have statements including return
+ createMethod.Statements.Count.ShouldBeGreaterThan(0);
+
+ var returns = createMethod.Statements.OfType().ToList();
+ returns.Count.ShouldBe(1);
+ }
+
+ [TestMethod]
+ public void ImplicitObjectCreation_WithNestedTypes_Should_BeDetected()
+ {
+ // Assign
+ var source = @"
+ class Test
+ {
+ void Method()
+ {
+ var dict = new System.Collections.Generic.Dictionary>();
+ System.Collections.Generic.Dictionary dict2 = new();
+ }
+ }
+ ";
+
+ // Act
+ var types = TestHelper.VisitSyntaxTree(source);
+
+ // Assert
+ var method = types[0].Methods[0];
+ method.Statements.Count.ShouldBe(2);
+
+ method.Statements[0].ShouldBeOfType();
+ method.Statements[1].ShouldBeOfType();
+
+ var invocation1 = (InvocationDescription)method.Statements[0];
+ var invocation2 = (InvocationDescription)method.Statements[1];
+
+ // Both should be detected as Dictionary creations
+ invocation1.ContainingType.ShouldContain("Dictionary");
+ invocation2.ContainingType.ShouldContain("Dictionary");
+ }
+
+ [TestMethod]
+ public void ImplicitObjectCreation_WithCollectionInitializers_Should_BeDetected()
+ {
+ // Assign
+ var source = @"
+ class Test
+ {
+ void Method()
+ {
+ var list = new System.Collections.Generic.List { 1, 2, 3, 4, 5 };
+ var dict = new System.Collections.Generic.Dictionary
+ {
+ [""one""] = 1,
+ [""two""] = 2
+ };
+ }
+ }
+ ";
+
+ // Act
+ var types = TestHelper.VisitSyntaxTree(source);
+
+ // Assert
+ var method = types[0].Methods[0];
+
+ // Should detect object creations
+ method.Statements.Count.ShouldBeGreaterThan(0);
+
+ var invocations = method.Statements.OfType().ToList();
+ invocations.Count.ShouldBeGreaterThan(0);
+
+ // Should have object creations for both collections
+ invocations.ShouldAllBe(inv => inv.Arguments.Count >= 0);
+ }
+
+ [TestMethod]
+ public void ImplicitObjectCreation_WithAnonymousTypes_Should_Work()
+ {
+ // Assign
+ var source = @"
+ class Test
+ {
+ void Method()
+ {
+ var anon = new { Name = ""Test"", Value = 42 };
+ var list = new System.Collections.Generic.List