diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 94dd82a..8c13831 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -9,6 +9,7 @@
+
diff --git a/src/Spectre.Console/Internal/TypeConverterHelper.cs b/src/Spectre.Console/Internal/TypeConverterHelper.cs
index 03c393e..6828c7c 100644
--- a/src/Spectre.Console/Internal/TypeConverterHelper.cs
+++ b/src/Spectre.Console/Internal/TypeConverterHelper.cs
@@ -2,6 +2,11 @@ namespace Spectre.Console;
internal static class TypeConverterHelper
{
+ internal const DynamicallyAccessedMemberTypes ConverterAnnotation = DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields;
+
+ internal static bool IsGetConverterSupported =>
+ !AppContext.TryGetSwitch("Spectre.Console.TypeConverterHelper.IsGetConverterSupported ", out var enabled) || enabled;
+
public static string ConvertToString(T input)
{
var result = GetTypeConverter().ConvertToInvariantString(input);
@@ -51,7 +56,7 @@ internal static class TypeConverterHelper
public static TypeConverter GetTypeConverter()
{
- var converter = TypeDescriptor.GetConverter(typeof(T));
+ var converter = GetConverter();
if (converter != null)
{
return converter;
@@ -72,5 +77,91 @@ internal static class TypeConverterHelper
}
throw new InvalidOperationException("Could not find type converter");
+
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2087", Justification = "Feature switches are not currently supported in the analyzer")]
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = "Feature switches are not currently supported in the analyzer")]
+ static TypeConverter? GetConverter()
+ {
+ if (!IsGetConverterSupported)
+ {
+ return GetIntrinsicConverter(typeof(T));
+ }
+
+ return TypeDescriptor.GetConverter(typeof(T));
+ }
+ }
+
+ private delegate TypeConverter FuncWithDam([DynamicallyAccessedMembers(ConverterAnnotation)] Type type);
+
+ private static readonly Dictionary _intrinsicConverters;
+
+ static TypeConverterHelper()
+ {
+ _intrinsicConverters = new()
+ {
+ [typeof(bool)] = _ => new BooleanConverter(),
+ [typeof(byte)] = _ => new ByteConverter(),
+ [typeof(sbyte)] = _ => new SByteConverter(),
+ [typeof(char)] = _ => new CharConverter(),
+ [typeof(double)] = _ => new DoubleConverter(),
+ [typeof(string)] = _ => new StringConverter(),
+ [typeof(int)] = _ => new Int32Converter(),
+ [typeof(short)] = _ => new Int16Converter(),
+ [typeof(long)] = _ => new Int64Converter(),
+ [typeof(float)] = _ => new SingleConverter(),
+ [typeof(ushort)] = _ => new UInt16Converter(),
+ [typeof(uint)] = _ => new UInt32Converter(),
+ [typeof(ulong)] = _ => new UInt64Converter(),
+ [typeof(object)] = _ => new TypeConverter(),
+ [typeof(CultureInfo)] = _ => new CultureInfoConverter(),
+ [typeof(DateTime)] = _ => new DateTimeConverter(),
+ [typeof(DateTimeOffset)] = _ => new DateTimeOffsetConverter(),
+ [typeof(decimal)] = _ => new DecimalConverter(),
+ [typeof(TimeSpan)] = _ => new TimeSpanConverter(),
+ [typeof(Guid)] = _ => new GuidConverter(),
+ [typeof(Uri)] = _ => new UriTypeConverter(),
+ [typeof(Array)] = _ => new ArrayConverter(),
+ [typeof(ICollection)] = _ => new CollectionConverter(),
+ [typeof(Enum)] = CreateEnumConverter(),
+ #if !NETSTANDARD2_0
+ [typeof(Int128)] = _ => new Int128Converter(),
+ [typeof(Half)] = _ => new HalfConverter(),
+ [typeof(UInt128)] = _ => new UInt128Converter(),
+ [typeof(DateOnly)] = _ => new DateOnlyConverter(),
+ [typeof(TimeOnly)] = _ => new TimeOnlyConverter(),
+ [typeof(Version)] = _ => new VersionConverter(),
+ #endif
+ };
+ }
+
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2111", Justification = "Delegate reflection is safe for all usages in this type.")]
+ private static FuncWithDam CreateEnumConverter() => ([DynamicallyAccessedMembers(ConverterAnnotation)] Type type) => new EnumConverter(type);
+
+ ///
+ /// A highly-constrained version of that only returns intrinsic converters.
+ ///
+ private static TypeConverter? GetIntrinsicConverter([DynamicallyAccessedMembers(ConverterAnnotation)] Type type)
+ {
+ if (type.IsArray)
+ {
+ type = typeof(Array);
+ }
+
+ if (typeof(ICollection).IsAssignableFrom(type))
+ {
+ type = typeof(ICollection);
+ }
+
+ if (type.IsEnum)
+ {
+ type = typeof(Enum);
+ }
+
+ if (_intrinsicConverters.TryGetValue(type, out var factory))
+ {
+ return factory(type);
+ }
+
+ return null;
}
}
\ No newline at end of file
diff --git a/src/Spectre.Console/Spectre.Console.csproj b/src/Spectre.Console/Spectre.Console.csproj
index 27bcc02..2c00fd5 100644
--- a/src/Spectre.Console/Spectre.Console.csproj
+++ b/src/Spectre.Console/Spectre.Console.csproj
@@ -23,7 +23,7 @@
all
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/src/Spectre.Console/Widgets/Exceptions/ExceptionFormatter.cs b/src/Spectre.Console/Widgets/Exceptions/ExceptionFormatter.cs
index a7e530c..383e619 100644
--- a/src/Spectre.Console/Widgets/Exceptions/ExceptionFormatter.cs
+++ b/src/Spectre.Console/Widgets/Exceptions/ExceptionFormatter.cs
@@ -1,12 +1,15 @@
namespace Spectre.Console;
-#pragma warning disable IL2026,IL2070,IL2075,IL3050
-
+// ExceptionFormatter relies heavily on reflection of types unknown until runtime.
+// We'll suppress these warnings, but alert the user this method is not supported.
+[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2026:RequiresUnreferencedCode")]
+[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2070:RequiresUnreferencedCode")]
+[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL2075:RequiresUnreferencedCode")]
+[RequiresDynamicCode(AotWarning)]
internal static class ExceptionFormatter
{
public const string AotWarning = "ExceptionFormatter is currently not supported for AOT.";
- [RequiresDynamicCode(ExceptionFormatter.AotWarning)]
public static IRenderable Format(Exception exception, ExceptionSettings settings)
{
if (exception is null)