@@ -56,14 +56,35 @@ private ulong fnvHash(string data_)
5656 }
5757
5858 return hash ;
59- }
59+ }
60+
61+ // Deterministic replacement for GetHashCode(), which is reference based
62+ // for syntax nodes and randomized per process for strings. Both would
63+ // give different ids for the same entity on every parse.
64+ private long stableHash ( string data_ )
65+ {
66+ return unchecked ( ( long ) fnvHash ( data_ ) ) ;
67+ }
68+
69+ private long getEntityHash ( SyntaxNode node )
70+ {
71+ string [ ] properties =
72+ {
73+ node . SyntaxTree . FilePath , ":" ,
74+ node . Kind ( ) . ToString ( ) , ":" ,
75+ node . Span . Start . ToString ( ) , ":" ,
76+ node . Span . End . ToString ( )
77+ } ;
78+
79+ return stableHash ( string . Concat ( properties ) ) ;
80+ }
6081
6182 private ulong getAstNodeId ( SyntaxNode node ) {
6283 CsharpAstNode astNode = new CsharpAstNode
6384 {
6485 AstValue = node . ToString ( ) ,
6586 RawKind = node . Kind ( ) ,
66- EntityHash = node . GetHashCode ( ) ,
87+ EntityHash = getEntityHash ( node ) ,
6788 AstType = AstTypeEnum . Declaration
6889 } ;
6990 astNode . SetLocation ( node . SyntaxTree . GetLineSpan ( node . Span ) ) ;
@@ -86,7 +107,7 @@ private CsharpAstNode AstNode(SyntaxNode node, AstSymbolTypeEnum type, AstTypeEn
86107 {
87108 AstValue = node . ToString ( ) ,
88109 RawKind = node . Kind ( ) ,
89- EntityHash = node . GetHashCode ( ) ,
110+ EntityHash = getEntityHash ( node ) ,
90111 AstSymbolType = type ,
91112 AstType = astType ,
92113 Accessibility = acc
@@ -616,7 +637,7 @@ private void VisitMethodDecl(MethodDeclarationSyntax node, CsharpAstNode parent)
616637 QualifiedName = qName ,
617638 QualifiedType = qType ,
618639 DocumentationCommentXML = Model . GetDeclaredSymbol ( node ) . GetDocumentationCommentXml ( ) ,
619- TypeHash = qType . GetHashCode ( ) ,
640+ TypeHash = stableHash ( qType ) ,
620641 EntityHash = astNode . EntityHash ,
621642 ParentNode = parent ,
622643 MethodType = MethodTypeEnum . Method
@@ -666,7 +687,7 @@ private void VisitOperatorDecl(OperatorDeclarationSyntax node, CsharpAstNode par
666687 QualifiedName = qName ,
667688 QualifiedType = qType ,
668689 DocumentationCommentXML = Model . GetDeclaredSymbol ( node ) . GetDocumentationCommentXml ( ) ,
669- TypeHash = qType . GetHashCode ( ) ,
690+ TypeHash = stableHash ( qType ) ,
670691 EntityHash = astNode . EntityHash ,
671692 ParentNode = parent ,
672693 MethodType = MethodTypeEnum . Operator
@@ -704,7 +725,7 @@ private void VisitMethodParameters(SeparatedSyntaxList<ParameterSyntax> paramete
704725 AstNode = astNode ,
705726 Name = param . Identifier . Text ,
706727 QualifiedType = paramQType ,
707- TypeHash = paramQType . GetHashCode ( ) ,
728+ TypeHash = stableHash ( paramQType ) ,
708729 EntityHash = astNode . EntityHash ,
709730 ParentNode = parent ,
710731 VariableType = VariableTypeEnum . Parameter
@@ -752,7 +773,7 @@ private void VisitVariableDecl(VariableDeclarationSyntax node, CsharpAstNode par
752773 AstNode = astNode ,
753774 Name = variable . Identifier . Text ,
754775 QualifiedType = varQType ,
755- TypeHash = varQType . GetHashCode ( ) ,
776+ TypeHash = stableHash ( varQType ) ,
756777 DocumentationCommentXML = Model . GetDeclaredSymbol ( variable ) . GetDocumentationCommentXml ( ) ,
757778 EntityHash = astNode . EntityHash ,
758779 VariableType = isLINQvar ? VariableTypeEnum . LINQ : VariableTypeEnum . Variable ,
@@ -779,7 +800,7 @@ private void VisitPropertyDecl(PropertyDeclarationSyntax node, CsharpAstNode par
779800 AstNode = astNode ,
780801 Name = node . Identifier . Text ,
781802 QualifiedType = varQType ,
782- TypeHash = varQType . GetHashCode ( ) ,
803+ TypeHash = stableHash ( varQType ) ,
783804 VariableType = VariableTypeEnum . Property ,
784805 DocumentationCommentXML = Model . GetDeclaredSymbol ( node ) . GetDocumentationCommentXml ( ) ,
785806 EntityHash = astNode . EntityHash ,
@@ -951,11 +972,69 @@ public override void VisitInvocationExpression(InvocationExpressionSyntax node)
951972 DbContext . CsharpEtcEntitys . Add ( invoc ) ;
952973 }
953974 }
954- }
955- }
975+ }
976+ }
977+
978+ VisitMethodInvocation ( node ) ;
956979
957980 base . VisitInvocationExpression ( node ) ;
958- }
981+ }
982+
983+ // Records an invocation whose DeclaratorNodeId points to the declaration
984+ // of the called method. This is what the Callee / Caller references and
985+ // the function call diagram rely on.
986+ private void VisitMethodInvocation ( InvocationExpressionSyntax node )
987+ {
988+ SimpleNameSyntax name = node . Expression switch
989+ {
990+ MemberAccessExpressionSyntax memberAccess => memberAccess . Name ,
991+ MemberBindingExpressionSyntax memberBinding => memberBinding . Name ,
992+ SimpleNameSyntax simpleName => simpleName ,
993+ _ => null
994+ } ;
995+
996+ // Unqualified Foo() calls are already recorded by VisitIdentifierName.
997+ if ( name == null || ( name == node . Expression && name is IdentifierNameSyntax ) )
998+ return ;
999+
1000+ var method = Model . GetSymbolInfo ( node ) . Symbol as IMethodSymbol ;
1001+ if ( method == null )
1002+ return ;
1003+
1004+ method = ( method . ReducedFrom ?? method ) . OriginalDefinition ;
1005+ method = method . PartialImplementationPart ?? method ;
1006+
1007+ var declaration = method . DeclaringSyntaxReferences
1008+ . Select ( r => r . GetSyntax ( ) )
1009+ . FirstOrDefault ( s => s is BaseMethodDeclarationSyntax ) ;
1010+ if ( declaration == null )
1011+ return ;
1012+
1013+ var type = "" ;
1014+ try
1015+ {
1016+ var info = Model . GetTypeInfo ( node ) . ConvertedType ;
1017+ if ( info != null ) type = info . Name ;
1018+ }
1019+ catch ( Exception )
1020+ {
1021+ FullyParsed = false ;
1022+ }
1023+
1024+ var declaratorNodeId = getAstNodeId ( declaration ) ;
1025+ var astNode = AstNode ( name , AstSymbolTypeEnum . EtcEntity , AstTypeEnum . Usage ) ;
1026+ CsharpEtcEntity invoc = new CsharpEtcEntity
1027+ {
1028+ AstNode = astNode ,
1029+ EntityHash = astNode . EntityHash ,
1030+ EtcEntityType = EtcEntityTypeEnum . Invocation ,
1031+ DeclaratorNodeId = declaratorNodeId ,
1032+ Name = name . Identifier . Text ,
1033+ QualifiedName = method . ToString ( ) ,
1034+ QualifiedType = type
1035+ } ;
1036+ DbContext . CsharpEtcEntitys . Add ( invoc ) ;
1037+ }
9591038
9601039 public override void VisitIdentifierName ( IdentifierNameSyntax node )
9611040 {
0 commit comments