Files

253 lines
9.3 KiB
C#
Raw Permalink Normal View History

using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace PCL.Core.SourceGenerators;
public static class SharedExtensions
{
public static string ToLiteral(this string str) => SymbolDisplay.FormatLiteral(str, true);
public static string? ToPrimitive(this object? obj) => SymbolDisplay.FormatPrimitive(obj, true, false);
public static int GetDeclarationOrder(this ISymbol symbol)
{
var loc = symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax().GetLocation();
return loc?.SourceSpan.Start ?? int.MaxValue;
}
extension(INamedTypeSymbol type)
{
public bool IsPartial()
{
foreach (var decl in type.DeclaringSyntaxReferences)
{
if (decl.GetSyntax() is ClassDeclarationSyntax { Modifiers: { } modifiers } &&
modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
return true;
}
return false;
}
public bool IsNestedWithin(INamedTypeSymbol potentialContainer)
{
var t = type.ContainingType;
while (t is not null)
{
if (SymbolEqualityComparer.Default.Equals(t, potentialContainer))
return true;
t = t.ContainingType;
}
return false;
}
public bool IsAttribute()
{
var baseType = type.BaseType;
while (baseType is not null)
{
if (baseType.ToDisplayString() == "System.Attribute") return true;
baseType = baseType.BaseType;
}
return false;
}
public int GenerateTypeHeader(StringBuilder sb)
{
var ctnTypes = new Stack<INamedTypeSymbol>();
for (var ctnType = type.ContainingType; ctnType is not null; ctnType = ctnType.ContainingType) ctnTypes.Push(ctnType);
// namespace
var ns = type.ContainingNamespace?.ToDisplayString();
var indent = 0;
if (!string.IsNullOrEmpty(ns))
{
sb.Append("namespace ").Append(ns).AppendLine();
sb.AppendLine("{");
indent++;
}
// outer classes
foreach (var containingType in ctnTypes)
{
sb.Append(' ', indent * 4).Append("partial class ").Append(containingType.Name).AppendLine();
sb.Append(' ', indent * 4).AppendLine("{");
indent++;
}
// class
sb.Append(' ', indent * 4).Append("partial class ").Append(type.Name).AppendLine();
sb.Append(' ', indent * 4).AppendLine("{");
return indent + 1;
}
}
public static string RenderDefaultValueCode(this SemanticModel sm, ExpressionSyntax expr)
{
if (expr is LiteralExpressionSyntax || expr.IsNegativeNumeric())
return expr.ToString();
if (expr is TypeOfExpressionSyntax toe)
{
var type = sm.GetTypeInfo(toe.Type).Type;
if (type is not null)
return "typeof(" + type.GetFullyQualifiedName() + ")";
return expr.ToString();
}
if (expr is InvocationExpressionSyntax
{
Expression: IdentifierNameSyntax { Identifier.ValueText: "nameof" },
ArgumentList.Arguments.Count: 1
} inv)
{
var targetExpr = inv.ArgumentList.Arguments[0].Expression;
var sym = sm.GetSymbolInfo(targetExpr).Symbol;
if (sym is not null)
{
return "nameof(" + sym.GetQualifiedSymbolName() + ")";
}
return expr.ToString();
}
var s = sm.GetSymbolInfo(expr).Symbol;
if (s is IFieldSymbol fs)
{
return fs.GetQualifiedSymbolName();
}
return expr.ToString();
}
public static bool IsNegativeNumeric(this ExpressionSyntax expr)
{
return expr is PrefixUnaryExpressionSyntax p
&& p.IsKind(SyntaxKind.UnaryMinusExpression)
&& p.Operand is LiteralExpressionSyntax l
&& l.IsKind(SyntaxKind.NumericLiteralExpression);
}
extension(ISymbol symbol)
{
public string GetQualifiedSymbolName()
{
if (symbol is ITypeSymbol ts) return ts.GetFullyQualifiedName();
var parts = new Stack<string>();
parts.Push(symbol.Name);
var t = symbol.ContainingType;
while (t is not null)
{
parts.Push(t.Name);
t = t.ContainingType;
}
var ns = symbol.ContainingNamespace?.ToDisplayString();
if (!string.IsNullOrEmpty(ns)) parts.Push(ns!);
return string.Join(".", parts);
}
}
private static readonly SymbolDisplayFormat _SimplifiedTypeNameFormat = new(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
miscellaneousOptions:
SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers |
SymbolDisplayMiscellaneousOptions.CollapseTupleTypes |
SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier |
SymbolDisplayMiscellaneousOptions.UseSpecialTypes,
genericsOptions: SymbolDisplayGenericsOptions.None
);
private static readonly SymbolDisplayFormat _FullQualifiedNameFormat = new(
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions:
SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers |
SymbolDisplayMiscellaneousOptions.UseSpecialTypes
);
extension(ITypeSymbol type)
{
public string GetSimplifiedTypeName()
{
return type.ToDisplayString(_SimplifiedTypeNameFormat);
}
public string GetFullyQualifiedName()
{
if (type is INamedTypeSymbol {
OriginalDefinition.SpecialType: SpecialType.System_Nullable_T,
TypeArguments.Length: 1 } nt)
{
var inner = nt.TypeArguments[0];
return inner.GetFullyQualifiedName() + "?";
}
if (type.TryGetSpecialTypeKeyword(out var keyword)) return keyword;
return type.ToDisplayString(_FullQualifiedNameFormat);
}
public bool TryGetSpecialTypeKeyword(out string keyword)
{
switch (type.SpecialType)
{
case SpecialType.System_Boolean: keyword = "bool"; return true;
case SpecialType.System_Byte: keyword = "byte"; return true;
case SpecialType.System_SByte: keyword = "sbyte"; return true;
case SpecialType.System_Int16: keyword = "short"; return true;
case SpecialType.System_UInt16: keyword = "ushort"; return true;
case SpecialType.System_Int32: keyword = "int"; return true;
case SpecialType.System_UInt32: keyword = "uint"; return true;
case SpecialType.System_Int64: keyword = "long"; return true;
case SpecialType.System_UInt64: keyword = "ulong"; return true;
case SpecialType.System_IntPtr: keyword = "nint"; return true;
case SpecialType.System_UIntPtr: keyword = "nuint"; return true;
case SpecialType.System_Char: keyword = "char"; return true;
case SpecialType.System_String: keyword = "string"; return true;
case SpecialType.System_Object: keyword = "object"; return true;
case SpecialType.System_Single: keyword = "float"; return true;
case SpecialType.System_Double: keyword = "double"; return true;
case SpecialType.System_Decimal: keyword = "decimal"; return true;
default: keyword = ""; return false;
}
}
}
public static string GetQualifiedPropertyAccess(this IPropertySymbol prop)
{
var owner = prop.ContainingType.GetFullyQualifiedName();
return owner + "." + prop.Name;
}
public static bool IsAwaitable(this IMethodSymbol method)
{
// TODO this is a very naive implementation.
return method.ReturnType.GetSimplifiedTypeName() == "System.Threading.Tasks.Task";
}
public static string CorrectConfigTypeName(this string typeName, out string? fullTypeName)
{
var isArgConfig = typeName.StartsWith("PCL.Core.App.Configuration.ArgConfig<");
if (isArgConfig)
{
fullTypeName = typeName;
typeName = typeName.Substring(37, typeName.Length - 38);
}
else fullTypeName = null;
return typeName;
}
extension(string str)
{
public string SnakeIdToPascal()
{
var sb = new StringBuilder();
foreach (var part in str.Split('-'))
{
if (part.Length == 0) continue;
sb.Append(char.ToUpper(part[0])).Append(part.Substring(1));
}
return sb.ToString();
}
}
}