Use fully qualified names to reduce namespace clashes. (#68)

* Use fully qualified names to reduce namespace clashes.

* Small code style fixes.

* Make properties in ProxyData immutable.

* Remove clutter by joining TrimEnd() to previous line.

* Introduce Extension method to retrieve ITypeSymbol FullyQualifiedDisplayString

* Fixed some code issues.

* Fixed method call in BaseGenerator

* Refactor metadata name
This commit is contained in:
David
2024-04-28 10:25:50 +02:00
committed by GitHub
parent 68864378d0
commit 39d85588e6
56 changed files with 1123 additions and 1146 deletions
@@ -11,8 +11,6 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators;
internal abstract class BaseGenerator
{
private const string Star = "*";
protected readonly Context Context;
protected readonly bool SupportsNullable;
@@ -34,25 +32,8 @@ internal abstract class BaseGenerator
protected bool TryFindProxyDataByTypeName(string type, [NotNullWhen(true)] out ProxyData? proxyData)
{
proxyData = Context.Candidates.Values.FirstOrDefault(x => x.FullRawTypeName == type);
if (proxyData != null)
{
return true;
}
foreach (var ci in Context.Candidates.Values)
{
foreach (var u in ci.Usings)
{
if ($"{u}.{ci.FullRawTypeName}" == type)
{
proxyData = ci;
return true;
}
}
}
return false;
proxyData = Context.Candidates.Values.FirstOrDefault(x => x.FullQualifiedTypeName == type);
return proxyData != null;
}
protected string GetWhereStatementFromMethod(IMethodSymbol method)
@@ -128,7 +109,7 @@ internal abstract class BaseGenerator
constraints.Add("new()");
}
if (constraints.Any())
if (constraints.Count > 0)
{
constraint = new(typeParameterSymbol.Name, constraints);
return true;
@@ -138,11 +119,21 @@ internal abstract class BaseGenerator
return false;
}
internal readonly SymbolDisplayFormat NullableDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions:
SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers |
SymbolDisplayMiscellaneousOptions.UseSpecialTypes |
SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier);
protected string GetReplacedTypeAsString(ITypeSymbol typeSymbol, out bool isReplaced)
{
isReplaced = false;
var typeSymbolAsString = typeSymbol.ToString();
var typeSymbolAsString = typeSymbol.ToFullyQualifiedDisplayString();
var nullableTypeSymbolAsString = typeSymbol.ToDisplayString(NullableFlowState.None, NullableDisplayFormat);
if (TryFindProxyDataByTypeName(typeSymbolAsString, out var existing))
{
@@ -152,7 +143,7 @@ internal abstract class BaseGenerator
}
isReplaced = true;
return FixType(existing.FullInterfaceName);
return FixType(existing.FullInterfaceName, typeSymbol.NullableAnnotation);
}
ITypeSymbol[] typeArguments;
@@ -166,13 +157,13 @@ internal abstract class BaseGenerator
}
else
{
return FixType(typeSymbolAsString);
return FixType(typeSymbolAsString, typeSymbol.NullableAnnotation);
}
var propertyTypeAsStringToBeModified = typeSymbolAsString;
var propertyTypeAsStringToBeModified = nullableTypeSymbolAsString;
foreach (var typeArgument in typeArguments)
{
var typeArgumentAsString = typeArgument.ToString();
var typeArgumentAsString = typeArgument.ToFullyQualifiedDisplayString();
if (TryFindProxyDataByTypeName(typeArgumentAsString, out var existingTypeArgument))
{
@@ -187,15 +178,21 @@ internal abstract class BaseGenerator
}
}
return FixType(propertyTypeAsStringToBeModified);
return FixType(propertyTypeAsStringToBeModified, typeSymbol.NullableAnnotation);
}
protected bool TryGetNamedTypeSymbolByFullName(TypeKind kind, string name, IEnumerable<string> usings, [NotNullWhen(true)] out ClassSymbol? classSymbol)
{
classSymbol = default;
const string globalPrefix = "global::";
if (name.StartsWith(globalPrefix, StringComparison.Ordinal))
{
name = name.Substring(globalPrefix.Length);
}
// The GetTypeByMetadataName method returns null if no type matches the full name or if 2 or more types (in different assemblies) match the full name.
var symbol = Context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName(name);
if (symbol is not null && symbol.TypeKind == kind)
{
classSymbol = new ClassSymbol(symbol, symbol.GetBaseTypes(), symbol.AllInterfaces.ToList());
@@ -223,7 +220,14 @@ internal abstract class BaseGenerator
string? type = null;
if (includeType)
{
type = parameterSymbol.GetTypeEnum() == TypeEnum.Complex ? GetParameterType(parameterSymbol, out _) : parameterSymbol.Type.ToString();
if (parameterSymbol.GetTypeEnum() == TypeEnum.Complex)
{
type = GetParameterType(parameterSymbol, out _);
}
else
{
type = FixType(parameterSymbol.Type.ToFullyQualifiedDisplayString(), parameterSymbol.NullableAnnotation);
}
}
methodParameters.Add(MethodParameterBuilder.Build(parameterSymbol, type));
@@ -237,36 +241,22 @@ internal abstract class BaseGenerator
var extendsProxyClasses = new List<ProxyData>();
foreach (var baseType in targetClassSymbol.BaseTypes)
{
var candidate = Context.Candidates.Values.FirstOrDefault(ci => ci.FullRawTypeName == baseType.ToString());
var candidate = Context.Candidates.Values.FirstOrDefault(ci => ci.FullQualifiedTypeName == baseType.ToFullyQualifiedDisplayString());
if (candidate is not null)
{
extendsProxyClasses.Add(candidate);
break;
}
// Try to find with usings
foreach (var @using in proxyData.Usings)
{
candidate = Context.Candidates.Values.FirstOrDefault(ci => $"{@using}.{ci.FullRawTypeName}" == baseType.ToString());
if (candidate is not null)
{
// Update the FullRawTypeName
candidate.FullRawTypeName = $"{@using}.{candidate.FullRawTypeName}";
extendsProxyClasses.Add(candidate);
break;
}
}
}
return extendsProxyClasses;
}
/// <summary>
/// Issue 54
/// double[*,*] --> double[,]
/// </summary>
protected static string FixType(string type)
internal static string FixType(string type, NullableAnnotation nullableAnnotation)
{
return type.Replace(Star, string.Empty);
if (nullableAnnotation == NullableAnnotation.Annotated && !type.EndsWith("?", StringComparison.Ordinal))
{
return $"{type}?";
}
return type;
}
}
@@ -37,7 +37,7 @@ internal class PartialInterfacesGenerator : BaseGenerator, IFilesGenerator
return false;
}
if (!TryGetNamedTypeSymbolByFullName(TypeKind.Class, pd.FullTypeName, pd.Usings, out var targetClassSymbol))
if (!TryGetNamedTypeSymbolByFullName(TypeKind.Class, pd.FullMetadataTypeName, pd.Usings, out var targetClassSymbol))
{
return false;
}
@@ -45,7 +45,7 @@ internal class PartialInterfacesGenerator : BaseGenerator, IFilesGenerator
var interfaceName = ResolveInterfaceNameWithOptionalTypeConstraints(targetClassSymbol.Symbol, pd.ShortInterfaceName);
fileData = new FileData(
$"{sourceInterfaceSymbol.Symbol.GetFileName()}.g.cs",
$"{sourceInterfaceSymbol.Symbol.GetFullMetadataName()}.g.cs",
CreatePartialInterfaceCode(pd.Namespace, targetClassSymbol, interfaceName, pd)
);
@@ -60,10 +60,13 @@ internal class PartialInterfacesGenerator : BaseGenerator, IFilesGenerator
{
var extendsProxyClasses = GetExtendsProxyData(proxyData, classSymbol);
ImplementedInterfaces = classSymbol.Symbol.ResolveImplementedInterfaces(proxyData.ProxyBaseClasses);
var implementedInterfacesNames = ImplementedInterfaces.Select(i => i.ToDisplayString(NullableFlowState.None, SymbolDisplayFormat.FullyQualifiedFormat));
var implementedInterfacesNames = ImplementedInterfaces.Select(i => i.ToFullyQualifiedDisplayString());
var implements = implementedInterfacesNames.Any() ? $" : {string.Join(", ", implementedInterfacesNames)}" : string.Empty;
var @new = extendsProxyClasses.Any() ? "new " : string.Empty;
var (namespaceStart, namespaceEnd) = NamespaceBuilder.Build(ns);
var events = GenerateEvents(classSymbol, proxyData.ProxyBaseClasses);
var properties = GenerateProperties(classSymbol, proxyData.ProxyBaseClasses);
var methods = GenerateMethods(classSymbol, proxyData.ProxyBaseClasses).TrimEnd();
return $@"//----------------------------------------------------------------------------------------
// <auto-generated>
@@ -80,13 +83,11 @@ using System;
{namespaceStart}
public partial interface {interfaceName}{implements}
{{
{@new}{classSymbol.Symbol} _Instance {{ get; }}
{@new}{classSymbol} _Instance {{ get; }}
{GenerateProperties(classSymbol, proxyData.ProxyBaseClasses)}
{GenerateMethods(classSymbol, proxyData.ProxyBaseClasses)}
{GenerateEvents(classSymbol, proxyData.ProxyBaseClasses)}
{events +
properties +
methods}
}}
{namespaceEnd}
{SupportsNullable.IIf("#nullable restore")}";
@@ -137,7 +138,6 @@ using System;
str.AppendLine($" {getterSetter.Value.PropertyType} {propertyName} {getterSetter.Value.GetSet}");
str.AppendLine();
}
return str.ToString();
}
@@ -19,7 +19,7 @@ internal partial class ProxyClassesGenerator
foreach (var replacedType in Context.ReplacedTypes)
{
TryFindProxyDataByTypeName(replacedType.Key, out var fullTypeName);
var classNameProxy = $"{fullTypeName!.Namespace}.{fullTypeName.ShortTypeName}Proxy";
var classNameProxy = $"{fullTypeName!.NamespaceDot}{fullTypeName.ShortMetadataName}Proxy";
var instance = $"instance{(replacedType.Key + replacedType.Value).GetDeterministicHashCodeAsString()}";
var proxy = $"proxy{(replacedType.Value + replacedType.Key).GetDeterministicHashCodeAsString()}";
@@ -12,7 +12,7 @@ internal partial class ProxyClassesGenerator
foreach (var replacedType in Context.ReplacedTypes)
{
TryFindProxyDataByTypeName(replacedType.Key, out var fullTypeName);
var classNameProxy = $"{fullTypeName!.Namespace}.{fullTypeName.ShortTypeName}Proxy";
var classNameProxy = $"global::{fullTypeName!.NamespaceDot}{fullTypeName!.ShortMetadataName}Proxy";
var instance = $"instance{(replacedType.Key + replacedType.Value).GetDeterministicHashCodeAsString()}";
var proxy = $"proxy{(replacedType.Value + replacedType.Key).GetDeterministicHashCodeAsString()}";
@@ -32,19 +32,19 @@ internal partial class ProxyClassesGenerator : BaseGenerator, IFilesGenerator
{
fileData = default;
if (!TryGetNamedTypeSymbolByFullName(TypeKind.Class, pd.FullTypeName, pd.Usings, out var targetClassSymbol))
if (!TryGetNamedTypeSymbolByFullName(TypeKind.Class, pd.FullMetadataTypeName, pd.Usings, out var targetClassSymbol))
{
return false;
}
var interfaceName = ResolveInterfaceNameWithOptionalTypeConstraints(targetClassSymbol.Symbol, pd.ShortInterfaceName);
var interfaceName = ResolveInterfaceNameWithOptionalTypeConstraints(targetClassSymbol.Symbol, pd.FullInterfaceName);
var className = targetClassSymbol.Symbol.ResolveProxyClassName();
var constructorName = $"{targetClassSymbol.Symbol.Name}Proxy";
var extendsProxyClasses = GetExtendsProxyData(pd, targetClassSymbol);
fileData = new FileData(
$"{targetClassSymbol.Symbol.GetFileName()}Proxy.g.cs",
$"{targetClassSymbol.Symbol.GetFullMetadataName()}Proxy.g.cs",
CreateProxyClassCode(pd, targetClassSymbol, extendsProxyClasses, interfaceName, className, constructorName)
);
@@ -68,11 +68,11 @@ internal partial class ProxyClassesGenerator : BaseGenerator, IFilesGenerator
if (firstExtends is not null)
{
extends = $"{firstExtends.Namespace}.{firstExtends.ShortTypeName}Proxy, ";
extends = $"global::{firstExtends.NamespaceDot}{firstExtends.ShortMetadataName}Proxy, ";
@base = " : base(instance)";
@new = "new ";
instanceBaseDefinition = $"public {firstExtends.FullRawTypeName} _Instance{firstExtends.FullRawTypeName.GetLastPart()} {{ get; }}";
instanceBaseSetter = $"_Instance{firstExtends.FullRawTypeName.GetLastPart()} = instance;";
instanceBaseDefinition = $"public {firstExtends.FullQualifiedTypeName} _Instance{firstExtends.FullQualifiedTypeName.GetLastPart()} {{ get; }}";
instanceBaseSetter = $"_Instance{firstExtends.FullQualifiedTypeName.GetLastPart()} = instance;";
}
var @abstract = string.Empty; // targetClassSymbol.Symbol.IsAbstract ? "abstract " : string.Empty;
@@ -106,17 +106,12 @@ using System;
{namespaceStart}
{accessibility} {@abstract}partial class {className} : {extends}{interfaceName}
{{
public {@new}{targetClassSymbol.Symbol} _Instance {{ get; }}
public {@new}{targetClassSymbol} _Instance {{ get; }}
{instanceBaseDefinition}
{properties}
{methods}
{events}
{operators}
{events +
properties +
methods +
operators}
public {constructorName}({targetClassSymbol} instance){@base}
{{
_Instance = instance;
@@ -244,7 +239,7 @@ using System;
foreach (var ps in method.Parameters.Where(p => !p.IsRef()))
{
var type = FixType(ps.Type.ToString());
var type = FixType(ps.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), ps.Type.NullableAnnotation);
string normalOrMap = $" = {ps.GetSanitizedName()}";
if (ps.RefKind == RefKind.Out)
{
@@ -265,7 +260,7 @@ using System;
var methodName = method.GetMethodNameWithOptionalTypeParameters();
var alternateReturnVariableName = $"result_{methodName.GetDeterministicHashCodeAsString()}";
string instance = !method.IsStatic ? "_Instance" : $"{targetClassSymbol.Symbol}";
string instance = method.IsStatic ? targetClassSymbol.Symbol.ToFullyQualifiedDisplayString() : "_Instance";
if (returnTypeAsString == "void")
{