Skip to content

Commit 4b8e65c

Browse files
committed
ATR-973: added simple heuristic algorithms to resolve the best symbol candidate based on args count
1 parent 0d0dbd0 commit 4b8e65c

2 files changed

Lines changed: 68 additions & 9 deletions

File tree

src/Acuminator/Acuminator.Utilities/Roslyn/Semantic/ISymbolGenericUtils.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#nullable enable
2-
32
using System;
43
using System.Collections.Generic;
54
using System.Linq;
@@ -11,6 +10,7 @@
1110

1211
using Acuminator.Utilities.Common;
1312
using Acuminator.Utilities.Roslyn.Syntax;
13+
using System.Collections.Immutable;
1414

1515
namespace Acuminator.Utilities.Roslyn.Semantic
1616
{
@@ -28,6 +28,14 @@ public static bool IsReadOnly(this ISymbol symbol) =>
2828
_ => false
2929
};
3030

31+
public static ImmutableArray<IParameterSymbol>? Parameters(this ISymbol symbol) =>
32+
symbol.CheckIfNull() switch
33+
{
34+
IMethodSymbol method => method.Parameters,
35+
IPropertySymbol property => property.Parameters,
36+
_ => null
37+
};
38+
3139
public static bool IsReadOnly(this ITypeSymbol typeSymbol)
3240
{
3341
typeSymbol.ThrowOnNull();

src/Acuminator/Acuminator.Utilities/Roslyn/Semantic/SemanticModelUtils.cs

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics.CodeAnalysis;
34
using System.Linq;
45
using System.Threading;
5-
6-
using Microsoft.CodeAnalysis.CSharp;
7-
using Microsoft.CodeAnalysis;
6+
using System.Threading.Tasks;
87

98
using Acuminator.Utilities.Common;
10-
using System.Threading.Tasks;
11-
using System.Diagnostics.CodeAnalysis;
9+
using Acuminator.Utilities.Roslyn.Syntax;
10+
11+
using Microsoft.CodeAnalysis;
12+
using Microsoft.CodeAnalysis.CSharp;
13+
using Microsoft.CodeAnalysis.CSharp.Syntax;
1214

1315
namespace Acuminator.Utilities.Roslyn.Semantic
1416
{
@@ -52,12 +54,61 @@ public static class SemanticModelUtils
5254
/// <returns>
5355
/// The symbol or the first candidate symbol.
5456
/// </returns>
55-
public static ISymbol? GetSymbolOrBestCandidate(this SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellation)
57+
public static ISymbol? GetSymbolOrBestCandidate(this SemanticModel semanticModel, SyntaxNode node,
58+
CancellationToken cancellation)
5659
{
5760
node.ThrowOnNull();
58-
5961
var symbolInfo = semanticModel.CheckIfNull().GetSymbolInfo(node, cancellation);
60-
return symbolInfo.Symbol ?? symbolInfo.CandidateSymbols.FirstOrDefault();
62+
63+
// Fast paths
64+
if (symbolInfo.Symbol != null)
65+
return symbolInfo.Symbol;
66+
else if (symbolInfo.CandidateSymbols.Length == 1)
67+
return symbolInfo.CandidateSymbols[0];
68+
else if (symbolInfo.CandidateSymbols.IsDefaultOrEmpty ||
69+
symbolInfo.CandidateReason is not (CandidateReason.Inaccessible or
70+
CandidateReason.OverloadResolutionFailure or
71+
CandidateReason.Ambiguous))
72+
{
73+
return null;
74+
}
75+
76+
// Try to match symbol with node based on arguments count heuristic
77+
var argumentList = node.GetArgumentsList();
78+
79+
if (argumentList == null)
80+
return symbolInfo.CandidateSymbols.FirstOrDefault();
81+
82+
return GetBestCandidateHeuristicallyByArgsCount(symbolInfo, argumentList.Arguments.Count);
83+
}
84+
85+
private static ISymbol? GetBestCandidateHeuristicallyByArgsCount(in SymbolInfo symbolInfo, int argsCount)
86+
{
87+
int minSuitableParametersCount = int.MaxValue;
88+
ISymbol? heuristicBestCandidate = null;
89+
90+
foreach (ISymbol candidate in symbolInfo.CandidateSymbols)
91+
{
92+
var parameters = candidate.Parameters();
93+
94+
if (parameters == null) // symbol doesn't have parameters
95+
continue;
96+
97+
int parametersCount = parameters.Value.Length;
98+
99+
if (argsCount > parametersCount)
100+
continue;
101+
else if (argsCount == parametersCount)
102+
return candidate; // perfect match
103+
else if (minSuitableParametersCount > parametersCount)
104+
{
105+
// Keep the overload with fewest parameters
106+
minSuitableParametersCount = parametersCount;
107+
heuristicBestCandidate = candidate;
108+
}
109+
}
110+
111+
return heuristicBestCandidate ?? symbolInfo.CandidateSymbols.FirstOrDefault();
61112
}
62113

63114
[SuppressMessage("Usage", "VSTHRD103:Call async methods when in an async method", Justification = "Aggregated await is used")]

0 commit comments

Comments
 (0)