/
MethodParameterLookupBase.cs
134 lines (121 loc) · 5.67 KB
/
MethodParameterLookupBase.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
* SonarAnalyzer for .NET
* Copyright (C) 2015-2023 SonarSource SA
* mailto: contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
namespace SonarAnalyzer.Helpers
{
public interface IMethodParameterLookup
{
bool TryGetSymbol(SyntaxNode argument, out IParameterSymbol parameter);
bool TryGetSyntax(IParameterSymbol parameter, out ImmutableArray<SyntaxNode> expressions);
bool TryGetSyntax(string parameterName, out ImmutableArray<SyntaxNode> expressions);
bool TryGetNonParamsSyntax(IParameterSymbol parameter, out SyntaxNode expression);
}
// This should come from the Roslyn API (https://github.com/dotnet/roslyn/issues/9)
internal abstract class MethodParameterLookupBase<TArgumentSyntax> : IMethodParameterLookup
where TArgumentSyntax : SyntaxNode
{
private readonly SeparatedSyntaxList<TArgumentSyntax>? argumentList;
protected abstract SyntaxToken? GetNameColonArgumentIdentifier(TArgumentSyntax argument);
protected abstract SyntaxNode Expression(TArgumentSyntax argument);
public IMethodSymbol MethodSymbol { get; }
protected MethodParameterLookupBase(SeparatedSyntaxList<TArgumentSyntax>? argumentList, IMethodSymbol methodSymbol)
{
this.argumentList = argumentList;
MethodSymbol = methodSymbol;
}
public bool TryGetSymbol(SyntaxNode argument, out IParameterSymbol parameter)
{
parameter = null;
var arg = argument as TArgumentSyntax ?? throw new ArgumentException($"{nameof(argument)} must be of type {typeof(TArgumentSyntax)}", nameof(argument));
if (!argumentList.HasValue
|| !argumentList.Value.Contains(arg)
|| MethodSymbol == null
|| MethodSymbol.IsVararg)
{
return false;
}
if (GetNameColonArgumentIdentifier(arg) is { } nameColonArgumentIdentifier)
{
parameter = MethodSymbol.Parameters.FirstOrDefault(symbol => symbol.Name == nameColonArgumentIdentifier.ValueText);
return parameter != null;
}
var index = argumentList.Value.IndexOf(arg);
if (index >= MethodSymbol.Parameters.Length)
{
var lastParameter = MethodSymbol.Parameters.Last();
parameter = lastParameter.IsParams ? lastParameter : null;
return parameter != null;
}
parameter = MethodSymbol.Parameters[index];
return true;
}
/// <summary>
/// Method returns array of argument syntaxes that represents all syntaxes passed to the parameter.
///
/// There could be multiple syntaxes for ParamArray/params.
/// There could be zero or one result for optional parameters.
/// There will be single result for normal parameters.
/// </summary>
public bool TryGetSyntax(IParameterSymbol parameter, out ImmutableArray<SyntaxNode> expressions) =>
TryGetSyntax(parameter.Name, out expressions);
/// <summary>
/// Method returns array of argument syntaxes that represents all syntaxes passed to the parameter.
///
/// There could be multiple syntaxes for ParamArray/params.
/// There could be zero or one result for optional parameters.
/// There will be single result for normal parameters.
public bool TryGetSyntax(string parameterName, out ImmutableArray<SyntaxNode> expressions)
{
expressions = GetAllArgumentParameterMappings().Where(x => x.Symbol.Name == parameterName).Select(x => Expression(x.Node)).ToImmutableArray();
return !expressions.IsEmpty;
}
/// <summary>
/// Method returns zero or one argument syntax that represents syntax passed to the parameter.
///
/// Caller must ensure that given parameter is not ParamArray/params.
/// </summary>
public bool TryGetNonParamsSyntax(IParameterSymbol parameter, out SyntaxNode expression)
{
if (parameter.IsParams)
{
throw new System.InvalidOperationException("Cannot call TryGetNonParamsSyntax on ParamArray/params parameters.");
}
if (TryGetSyntax(parameter, out var all))
{
expression = all.Single();
return true;
}
expression = null;
return false;
}
internal IEnumerable<NodeAndSymbol<TArgumentSyntax, IParameterSymbol>> GetAllArgumentParameterMappings()
{
if (argumentList.HasValue)
{
foreach (var argument in argumentList)
{
if (TryGetSymbol(argument, out var parameter))
{
yield return new NodeAndSymbol<TArgumentSyntax, IParameterSymbol>(argument, parameter);
}
}
}
}
}
}