@@ -27,15 +27,18 @@ public class CodeGraphWalker : CSharpSyntaxWalker
2727 private readonly HashSet < string > _classNames = new HashSet < string > ( ) ;
2828 private readonly HashSet < string > _methodFullNames = new HashSet < string > ( ) ;
2929 private readonly HashSet < string > _propertyFullNames = new HashSet < string > ( ) ;
30+ private readonly HashSet < string > _variableFullNames = new HashSet < string > ( ) ;
3031
3132 // Track used/unused elements
3233 private readonly HashSet < string > _usedClasses = new HashSet < string > ( ) ;
3334 private readonly HashSet < string > _usedMethods = new HashSet < string > ( ) ;
3435 private readonly HashSet < string > _usedProperties = new HashSet < string > ( ) ;
36+ private readonly HashSet < string > _usedVariables = new HashSet < string > ( ) ;
3537
3638 // Track method calls and property access
3739 private readonly Dictionary < string , List < string > > _methodCallMap = new Dictionary < string , List < string > > ( ) ;
3840 private readonly Dictionary < string , List < string > > _methodPropertyMap = new Dictionary < string , List < string > > ( ) ;
41+ private readonly Dictionary < string , string > _variableTypeMap = new Dictionary < string , string > ( ) ;
3942
4043 // Override to use custom parameters
4144 public CodeGraphWalker ( ) : base ( SyntaxWalkerDepth . Node )
@@ -214,10 +217,10 @@ public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node)
214217 }
215218
216219 // Record property->type
217- // if (!_propertyTypeMap .ContainsKey(fullPropertyName))
218- // {
219- // _propertyTypeMap [fullPropertyName] = typeFullName;
220- // }
220+ if ( ! _variableTypeMap . ContainsKey ( fullPropertyName ) )
221+ {
222+ _variableTypeMap [ fullPropertyName ] = typeFullName ;
223+ }
221224 }
222225
223226 base . VisitPropertyDeclaration ( node ) ;
@@ -236,7 +239,7 @@ public override void VisitLocalDeclarationStatement(LocalDeclarationStatementSyn
236239 {
237240 string variableName = variable . Identifier . Text ;
238241 string fullVariableName = $ "{ _currentMethod } .{ variableName } ";
239- // _variableFullNames.Add(fullVariableName);
242+ _variableFullNames . Add ( fullVariableName ) ;
240243
241244 // Get type information
242245 var typeSymbol = SemanticModel . GetTypeInfo ( node . Declaration . Type ) . Type ;
@@ -250,7 +253,7 @@ public override void VisitLocalDeclarationStatement(LocalDeclarationStatementSyn
250253 _usedClasses . Add ( typeFullName ) ;
251254 }
252255
253- // _variableTypeMap[fullVariableName] = typeFullName;
256+ _variableTypeMap [ fullVariableName ] = typeFullName ;
254257 }
255258 }
256259
@@ -282,8 +285,8 @@ public override void VisitFieldDeclaration(FieldDeclarationSyntax node)
282285 {
283286 string variableName = variable . Identifier . Text ;
284287 string fullVariableName = $ "{ _currentClass } .{ variableName } ";
285- // _variableFullNames.Add(fullVariableName);
286- // _variableTypeMap[fullVariableName] = typeFullName;
288+ _variableFullNames . Add ( fullVariableName ) ;
289+ _variableTypeMap [ fullVariableName ] = typeFullName ;
287290 }
288291 }
289292
@@ -406,6 +409,37 @@ public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax no
406409 base . VisitMemberAccessExpression ( node ) ;
407410 }
408411
412+ /// <summary>
413+ /// Visit identifier name expressions (e.g. variable references)
414+ /// </summary>
415+ public override void VisitIdentifierName ( IdentifierNameSyntax node )
416+ {
417+ // Only track identifiers within a method context
418+ if ( SemanticModel != null && ! string . IsNullOrEmpty ( _currentMethod ) )
419+ {
420+ string identifierName = node . Identifier . Text ;
421+ string fullVariableName = $ "{ _currentMethod } .{ identifierName } ";
422+
423+ // If this is a variable we're tracking, mark it as used
424+ if ( _variableFullNames . Contains ( fullVariableName ) )
425+ {
426+ _usedVariables . Add ( fullVariableName ) ;
427+ }
428+
429+ // Also check if it might be a class field
430+ if ( ! string . IsNullOrEmpty ( _currentClass ) )
431+ {
432+ string fieldName = $ "{ _currentClass } .{ identifierName } ";
433+ if ( _variableFullNames . Contains ( fieldName ) )
434+ {
435+ _usedVariables . Add ( fieldName ) ;
436+ }
437+ }
438+ }
439+
440+ base . VisitIdentifierName ( node ) ;
441+ }
442+
409443 /// <summary>
410444 /// Improves property access tracking by processing object initializers
411445 /// </summary>
@@ -474,48 +508,6 @@ public override void VisitObjectCreationExpression(ObjectCreationExpressionSynta
474508 base . VisitObjectCreationExpression ( node ) ;
475509 }
476510
477- // Helpers for building full names from symbols
478- private string BuildFullTypeName ( ITypeSymbol typeSymbol )
479- {
480- if ( typeSymbol == null )
481- return "Unknown" ;
482-
483- if ( typeSymbol . ContainingNamespace != null && ! string . IsNullOrEmpty ( typeSymbol . ContainingNamespace . Name ) )
484- {
485- return $ "{ typeSymbol . ContainingNamespace } .{ typeSymbol . Name } ";
486- }
487-
488- return typeSymbol . Name ;
489- }
490-
491- private string BuildFullMethodName ( IMethodSymbol methodSymbol )
492- {
493- if ( methodSymbol == null )
494- return "Unknown" ;
495-
496- if ( methodSymbol . ContainingType != null )
497- {
498- string typeName = BuildFullTypeName ( methodSymbol . ContainingType ) ;
499- return $ "{ typeName } .{ methodSymbol . Name } ";
500- }
501-
502- return methodSymbol . Name ;
503- }
504-
505- private string BuildFullPropertyName ( IPropertySymbol propertySymbol )
506- {
507- if ( propertySymbol == null )
508- return "Unknown" ;
509-
510- if ( propertySymbol . ContainingType != null )
511- {
512- string typeName = BuildFullTypeName ( propertySymbol . ContainingType ) ;
513- return $ "{ typeName } .{ propertySymbol . Name } ";
514- }
515-
516- return propertySymbol . Name ;
517- }
518-
519511 /// <summary>
520512 /// Generates the final D3 graph with all nodes and links
521513 /// </summary>
@@ -710,6 +702,18 @@ public D3Graph GetGraph()
710702 } ) ;
711703 }
712704
705+ // Add variable nodes
706+ foreach ( var variable in _variableFullNames )
707+ {
708+ graph . Nodes . Add ( new D3Node
709+ {
710+ Id = variable ,
711+ Group = "variable" ,
712+ Label = variable . Split ( '.' ) . Last ( ) ,
713+ Used = _usedVariables . Contains ( variable )
714+ } ) ;
715+ }
716+
713717 // Add external nodes for references that would be dangling otherwise
714718 foreach ( var externalNode in externalNodes )
715719 {
@@ -739,64 +743,88 @@ public D3Graph GetGraph()
739743
740744 // 4) Links:
741745 // (a) namespace -> class
742- foreach ( var cls in _classNames )
746+ foreach ( var className in _classNames )
743747 {
744- int idx = cls . LastIndexOf ( '.' ) ;
745- if ( idx > 0 )
748+ var namespaceName = className . Split ( '.' ) [ 0 ] ;
749+ graph . Links . Add ( new D3Link
746750 {
747- var ns = cls . Substring ( 0 , idx ) ;
748- if ( _namespaceNames . Contains ( ns ) )
751+ Source = namespaceName ,
752+ Target = className ,
753+ Type = "containment"
754+ } ) ;
755+ }
756+
757+ // (b) class -> method
758+ foreach ( var method in _methodFullNames )
759+ {
760+ int lastDot = method . LastIndexOf ( '.' ) ;
761+ if ( lastDot > 0 )
762+ {
763+ string className = method . Substring ( 0 , lastDot ) ;
764+ if ( _classNames . Contains ( className ) )
749765 {
750766 graph . Links . Add ( new D3Link
751767 {
752- Source = ns ,
753- Target = cls ,
768+ Source = className ,
769+ Target = method ,
754770 Type = "containment"
755771 } ) ;
756772 }
757773 }
758774 }
759-
760- // (b ) class -> method
761- foreach ( var method in _methodFullNames )
775+
776+ // (c ) class -> property
777+ foreach ( var property in _propertyFullNames )
762778 {
763- int idx = method . LastIndexOf ( '.' ) ;
764- if ( idx > 0 )
779+ int lastDot = property . LastIndexOf ( '.' ) ;
780+ if ( lastDot > 0 )
765781 {
766- var cls = method . Substring ( 0 , idx ) ;
767- if ( _classNames . Contains ( cls ) )
782+ string className = property . Substring ( 0 , lastDot ) ;
783+ if ( _classNames . Contains ( className ) )
768784 {
769785 graph . Links . Add ( new D3Link
770786 {
771- Source = cls ,
772- Target = method ,
787+ Source = className ,
788+ Target = property ,
773789 Type = "containment"
774790 } ) ;
775791 }
776792 }
777793 }
778794
779- // (c) class->property
780- foreach ( var prop in _propertyFullNames )
795+ // (d) method -> variable (variables belong to methods)
796+ foreach ( var variable in _variableFullNames )
781797 {
782- int idx = prop . LastIndexOf ( '.' ) ;
783- if ( idx > 0 )
798+ int lastDot = variable . LastIndexOf ( '.' ) ;
799+ if ( lastDot > 0 )
784800 {
785- var cls = prop . Substring ( 0 , idx ) ;
786- // If that class is in _classNames, link them
787- if ( _classNames . Contains ( cls ) )
801+ string container = variable . Substring ( 0 , lastDot ) ;
802+
803+ // Check if this is a method variable or class field
804+ if ( _methodFullNames . Contains ( container ) )
788805 {
806+ // Method variable
789807 graph . Links . Add ( new D3Link
790808 {
791- Source = cls ,
792- Target = prop ,
809+ Source = container ,
810+ Target = variable ,
811+ Type = "containment"
812+ } ) ;
813+ }
814+ else if ( _classNames . Contains ( container ) )
815+ {
816+ // Class field
817+ graph . Links . Add ( new D3Link
818+ {
819+ Source = container ,
820+ Target = variable ,
793821 Type = "containment"
794822 } ) ;
795823 }
796824 }
797825 }
798826
799- // (d ) method-> property usage
827+ // (e ) method -> property usage
800828 foreach ( var kvp in _methodPropertyMap )
801829 {
802830 var callerMethod = kvp . Key ; // e.g. "MyApp.Core.Foo.Bar"
@@ -817,26 +845,19 @@ public D3Graph GetGraph()
817845 }
818846 }
819847 }
820-
821- // (e ) method -> method (method calls)
848+
849+ // (f ) method calls
822850 foreach ( var kvp in _methodCallMap )
823851 {
824- var caller = kvp . Key ;
852+ string caller = kvp . Key ;
825853 foreach ( var callee in kvp . Value )
826854 {
827- // Make sure both ends of the link exist
828- if ( NodeExists ( graph , caller ) && NodeExists ( graph , callee ) )
855+ // Skip self-calls (they clutter the graph)
856+ if ( caller != callee )
829857 {
830- // default to internal call
831- var linkType = "call" ;
832-
833- // Check if this is an external call (a method outside our codebase)
834- bool isExternal = ! _methodFullNames . Contains ( callee ) ;
835- if ( isExternal )
836- {
837- linkType = "external" ;
838- }
839-
858+ // External calls get a different edge type
859+ string linkType = _methodFullNames . Contains ( callee ) ? "call" : "external" ;
860+
840861 graph . Links . Add ( new D3Link
841862 {
842863 Source = caller ,
@@ -857,6 +878,48 @@ private bool NodeExists(D3Graph graph, string nodeId)
857878 {
858879 return graph . Nodes . Any ( n => n . Id == nodeId ) ;
859880 }
881+
882+ // Helpers for building full names from symbols
883+ private string BuildFullTypeName ( ITypeSymbol typeSymbol )
884+ {
885+ if ( typeSymbol == null )
886+ return "Unknown" ;
887+
888+ if ( typeSymbol . ContainingNamespace != null && ! string . IsNullOrEmpty ( typeSymbol . ContainingNamespace . Name ) )
889+ {
890+ return $ "{ typeSymbol . ContainingNamespace } .{ typeSymbol . Name } ";
891+ }
892+
893+ return typeSymbol . Name ;
894+ }
895+
896+ private string BuildFullMethodName ( IMethodSymbol methodSymbol )
897+ {
898+ if ( methodSymbol == null )
899+ return "Unknown" ;
900+
901+ if ( methodSymbol . ContainingType != null )
902+ {
903+ string typeName = BuildFullTypeName ( methodSymbol . ContainingType ) ;
904+ return $ "{ typeName } .{ methodSymbol . Name } ";
905+ }
906+
907+ return methodSymbol . Name ;
908+ }
909+
910+ private string BuildFullPropertyName ( IPropertySymbol propertySymbol )
911+ {
912+ if ( propertySymbol == null )
913+ return "Unknown" ;
914+
915+ if ( propertySymbol . ContainingType != null )
916+ {
917+ string typeName = BuildFullTypeName ( propertySymbol . ContainingType ) ;
918+ return $ "{ typeName } .{ propertySymbol . Name } ";
919+ }
920+
921+ return propertySymbol . Name ;
922+ }
860923 }
861924
862925 /// <summary>
0 commit comments