Skip to content

Commit f449545

Browse files
committed
Simplify replacement of error type names with inferred names
Change approach to use `ToDisplayParts`, which removes the need for regex parsing of generated code
1 parent 6ccd23a commit f449545

File tree

1 file changed

+28
-68
lines changed

1 file changed

+28
-68
lines changed

AutomaticInterface/AutomaticInterface/RoslynExtensions.cs

Lines changed: 28 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -86,104 +86,64 @@ public static string ToDisplayString(
8686
this IParameterSymbol symbol,
8787
SymbolDisplayFormat displayFormat,
8888
List<string> generatedInterfaceNames
89-
)
90-
{
91-
var parameterDisplayString = symbol.ToDisplayString(displayFormat);
92-
93-
var parameterTypeDisplayString = symbol.Type.ToDisplayString(
94-
displayFormat,
95-
generatedInterfaceNames
96-
);
89+
) => ToDisplayString((ISymbol)symbol, displayFormat, generatedInterfaceNames);
9790

98-
// Replace the type part of the parameter definition - we don't try to generate the whole parameter definition
99-
// as it's quite complex - we leave that to Roslyn.
100-
return ParameterTypeMatcher.Replace(parameterDisplayString, parameterTypeDisplayString);
101-
}
102-
103-
/// <summary>
104-
/// Matches the type part of a parameter definition (Type name[ = defaultValue])
105-
/// </summary>
106-
private static readonly Regex ParameterTypeMatcher =
107-
new(@"[^\s=]+(?=\s\S+(\s?=\s?\S+)?$)", RegexOptions.Compiled);
108-
109-
/// <summary>
110-
/// Wraps <see cref="ITypeSymbol.ToDisplayString(Microsoft.CodeAnalysis.SymbolDisplayFormat?)" /> with custom resolution for generated types
111-
/// </summary>
112-
/// <returns></returns>
11391
public static string ToDisplayString(
11492
this ITypeSymbol symbol,
11593
SymbolDisplayFormat displayFormat,
11694
List<string> generatedInterfaceNames
117-
)
118-
{
119-
var builder = new StringBuilder();
120-
121-
AppendTypeSymbolDisplayString(symbol, displayFormat, generatedInterfaceNames, builder);
95+
) => ToDisplayString((ISymbol)symbol, displayFormat, generatedInterfaceNames);
12296

123-
return builder.ToString();
124-
}
125-
126-
private static void AppendTypeSymbolDisplayString(
127-
ITypeSymbol typeSymbol,
97+
/// <summary>
98+
/// Wraps <see cref="ITypeSymbol.ToDisplayString(Microsoft.CodeAnalysis.SymbolDisplayFormat?)" /> with custom resolution for generated types
99+
/// </summary>
100+
private static string ToDisplayString(
101+
this ISymbol symbol,
128102
SymbolDisplayFormat displayFormat,
129-
List<string> generatedInterfaceNames,
130-
StringBuilder builder
103+
List<string> generatedInterfaceNames
131104
)
132105
{
133-
if (typeSymbol is not IErrorTypeSymbol errorTypeSymbol)
134-
{
135-
// This symbol contains no unresolved types. Fall back to the default generation provided by Roslyn
136-
builder.Append(typeSymbol.ToDisplayString(displayFormat));
137-
return;
138-
}
106+
var displayStringBuilder = new StringBuilder();
139107

140-
var symbolName =
141-
InferGeneratedInterfaceName(errorTypeSymbol, generatedInterfaceNames)
142-
?? errorTypeSymbol.Name;
108+
var displayParts = symbol.ToDisplayParts(displayFormat);
143109

144-
builder.Append(symbolName);
145-
146-
if (errorTypeSymbol.IsGenericType)
110+
foreach (var part in displayParts)
147111
{
148-
builder.Append('<');
149-
150-
bool isFirstTypeArgument = true;
151-
foreach (var typeArgument in errorTypeSymbol.TypeArguments)
112+
if (part.Kind == SymbolDisplayPartKind.ErrorTypeName)
152113
{
153-
if (!isFirstTypeArgument)
154-
{
155-
builder.Append(", ");
156-
}
157-
158-
AppendTypeSymbolDisplayString(
159-
typeArgument,
160-
displayFormat,
161-
generatedInterfaceNames,
162-
builder
114+
var unrecognisedName = part.ToString();
115+
116+
var inferredName = ReplaceWithInferredInterfaceName(
117+
unrecognisedName,
118+
generatedInterfaceNames
163119
);
164120

165-
isFirstTypeArgument = false;
121+
displayStringBuilder.Append(inferredName);
122+
}
123+
else
124+
{
125+
displayStringBuilder.Append(part.ToString());
166126
}
167-
168-
builder.Append('>');
169127
}
128+
129+
return displayStringBuilder.ToString();
170130
}
171131

172-
private static string? InferGeneratedInterfaceName(
173-
IErrorTypeSymbol unrecognisedSymbol,
132+
private static string ReplaceWithInferredInterfaceName(
133+
string unrecognisedName,
174134
List<string> generatedInterfaceNames
175135
)
176136
{
177137
var matches = generatedInterfaceNames
178-
.Where(name => Regex.IsMatch(name, $"[.:]{unrecognisedSymbol.Name}$"))
138+
.Where(name => Regex.IsMatch(name, $"[.:]{unrecognisedName}$"))
179139
.ToList();
180140

181141
if (matches.Count != 1)
182142
{
183143
// Either there's no match or an ambiguous match - we can't safely infer the interface name.
184144
// This is very much a "best effort" approach - if there are two interfaces with the same name,
185145
// there's no obvious way to work out which one the symbol is referring to.
186-
return null;
146+
return unrecognisedName;
187147
}
188148

189149
return matches[0];

0 commit comments

Comments
 (0)