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:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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")
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user