1
+ using System . Collections . Generic ;
2
+ using System . Linq ;
3
+ using Microsoft . CodeAnalysis ;
4
+ using Microsoft . CodeAnalysis . Operations ;
5
+
6
+ namespace NSubstitute . Analyzers . Shared . DiagnosticAnalyzers
7
+ {
8
+ internal abstract class AbstractCallInfoFinder < TInvocationExpressionSyntax , TIndexerSyntax > : ICallInfoFinder < TInvocationExpressionSyntax , TIndexerSyntax >
9
+ where TInvocationExpressionSyntax : SyntaxNode where TIndexerSyntax : SyntaxNode
10
+ {
11
+ public CallInfoContext < TInvocationExpressionSyntax , TIndexerSyntax > GetCallInfoContext (
12
+ SemanticModel semanticModel , SyntaxNode syntaxNode )
13
+ {
14
+ var callInfoParameterSymbol = GetCallInfoParameterSymbol ( semanticModel , syntaxNode ) ;
15
+ if ( callInfoParameterSymbol == null )
16
+ {
17
+ return CallInfoContext < TInvocationExpressionSyntax , TIndexerSyntax > . Empty ;
18
+ }
19
+
20
+ var callContext = GetCallInfoContextInternal ( semanticModel , syntaxNode ) ;
21
+
22
+ return CreateFilteredCallInfoContext ( semanticModel , callContext , callInfoParameterSymbol ) ;
23
+ }
24
+
25
+ protected abstract CallInfoContext < TInvocationExpressionSyntax , TIndexerSyntax > GetCallInfoContextInternal ( SemanticModel semanticModel , SyntaxNode syntaxNode ) ;
26
+
27
+ private static CallInfoContext < TInvocationExpressionSyntax , TIndexerSyntax > CreateFilteredCallInfoContext (
28
+ SemanticModel semanticModel ,
29
+ CallInfoContext < TInvocationExpressionSyntax , TIndexerSyntax > callContext ,
30
+ IParameterSymbol callInfoParameterSymbol )
31
+ {
32
+ return new CallInfoContext < TInvocationExpressionSyntax , TIndexerSyntax > (
33
+ argAtInvocations : GetMatchingNodes ( semanticModel , callContext . ArgAtInvocations , callInfoParameterSymbol ) ,
34
+ argInvocations : GetMatchingNodes ( semanticModel , callContext . ArgInvocations , callInfoParameterSymbol ) ,
35
+ indexerAccesses : GetMatchingNodes ( semanticModel , callContext . IndexerAccesses , callInfoParameterSymbol ) ) ;
36
+ }
37
+
38
+ private static IReadOnlyList < T > GetMatchingNodes < T > (
39
+ SemanticModel semanticModel ,
40
+ IReadOnlyList < T > nodes ,
41
+ IParameterSymbol parameterSymbol ) where T : SyntaxNode
42
+ {
43
+ return nodes . Where ( node => HasMatchingParameterReference ( semanticModel , node , parameterSymbol ) ) . ToList ( ) ;
44
+ }
45
+
46
+ private static bool HasMatchingParameterReference (
47
+ SemanticModel semanticModel ,
48
+ SyntaxNode syntaxNode ,
49
+ IParameterSymbol callInfoParameterSymbol )
50
+ {
51
+ var parameterReferenceOperation = FindMatchingParameterReference ( semanticModel , syntaxNode ) ;
52
+
53
+ return parameterReferenceOperation != null &&
54
+ parameterReferenceOperation . Parameter . Equals ( callInfoParameterSymbol ) ;
55
+ }
56
+
57
+ private static IParameterReferenceOperation FindMatchingParameterReference ( SemanticModel semanticModel , SyntaxNode syntaxNode )
58
+ {
59
+ var operation = semanticModel . GetOperation ( syntaxNode ) ;
60
+ return FindMatchingParameterReference ( operation ) ;
61
+ }
62
+
63
+ private static IParameterReferenceOperation FindMatchingParameterReference ( IOperation operation )
64
+ {
65
+ IParameterReferenceOperation parameterReferenceOperation = null ;
66
+ switch ( operation )
67
+ {
68
+ case IInvocationOperation invocationOperation :
69
+ parameterReferenceOperation = invocationOperation . Instance as IParameterReferenceOperation ;
70
+ break ;
71
+ case IPropertyReferenceOperation propertyReferenceOperation :
72
+ parameterReferenceOperation = propertyReferenceOperation . Instance as IParameterReferenceOperation ;
73
+ break ;
74
+ }
75
+
76
+ if ( parameterReferenceOperation != null )
77
+ {
78
+ return parameterReferenceOperation ;
79
+ }
80
+
81
+ foreach ( var innerOperation in operation ? . Children ?? Enumerable . Empty < IOperation > ( ) )
82
+ {
83
+ parameterReferenceOperation = FindMatchingParameterReference ( innerOperation ) ;
84
+ if ( parameterReferenceOperation != null )
85
+ {
86
+ return parameterReferenceOperation ;
87
+ }
88
+ }
89
+
90
+ return null ;
91
+ }
92
+
93
+ private static IParameterSymbol GetCallInfoParameterSymbol ( SemanticModel semanticModel , SyntaxNode syntaxNode )
94
+ {
95
+ if ( semanticModel . GetSymbolInfo ( syntaxNode ) . Symbol is IMethodSymbol methodSymbol && methodSymbol . MethodKind != MethodKind . Constructor )
96
+ {
97
+ return methodSymbol . Parameters . FirstOrDefault ( ) ;
98
+ }
99
+
100
+ return null ;
101
+ }
102
+ }
103
+ }
0 commit comments