diff --git a/NewType.Generator/AliasCodeGenerator.cs b/NewType.Generator/AliasCodeGenerator.cs
index 51c4e44..5c7324f 100644
--- a/NewType.Generator/AliasCodeGenerator.cs
+++ b/NewType.Generator/AliasCodeGenerator.cs
@@ -751,28 +751,49 @@ private void AppendSerializationConverters()
_sb.AppendLine($"{indent}#region Serialization");
_sb.AppendLine();
+ // When T is string, typeof(T) == typeof(string); emit only the string arm to avoid
+ // duplicate conditions and unreachable branches.
+ var aliasedIsString = _model.AliasedTypeSpecialType == SpecialType.System_String;
+
// TypeConverter — covers Newtonsoft.Json, ASP.NET model binding, configuration binding, etc.
_sb.AppendLine($"{indent}/// Converts {name} to and from {minimal} and string.");
_sb.AppendLine($"{indent}public sealed class NewtypeTypeConverter : global::System.ComponentModel.TypeConverter");
_sb.AppendLine($"{indent}{{");
+ if (!aliasedIsString)
+ {
+ // Resolve the underlying type's converter once — TypeDescriptor lookups aren't free.
+ _sb.AppendLine($"{inner}private static readonly global::System.ComponentModel.TypeConverter _inner = global::System.ComponentModel.TypeDescriptor.GetConverter(typeof({t}));");
+ _sb.AppendLine();
+ }
_sb.AppendLine($"{inner}/// ");
_sb.AppendLine($"{inner}public override bool CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext? context, global::System.Type sourceType)");
- _sb.AppendLine($"{body}=> sourceType == typeof(string) || sourceType == typeof({t}) || base.CanConvertFrom(context, sourceType);");
+ _sb.AppendLine(aliasedIsString
+ ? $"{body}=> sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);"
+ : $"{body}=> sourceType == typeof(string) || sourceType == typeof({t}) || base.CanConvertFrom(context, sourceType);");
_sb.AppendLine();
_sb.AppendLine($"{inner}/// ");
_sb.AppendLine($"{inner}public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext? context, global::System.Type? destinationType)");
- _sb.AppendLine($"{body}=> destinationType == typeof(string) || destinationType == typeof({t}) || base.CanConvertTo(context, destinationType);");
+ _sb.AppendLine(aliasedIsString
+ ? $"{body}=> destinationType == typeof(string) || base.CanConvertTo(context, destinationType);"
+ : $"{body}=> destinationType == typeof(string) || destinationType == typeof({t}) || base.CanConvertTo(context, destinationType);");
_sb.AppendLine();
_sb.AppendLine($"{inner}/// ");
_sb.AppendLine($"{inner}public override object? ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext? context, global::System.Globalization.CultureInfo? culture, object value)");
_sb.AppendLine($"{inner}{{");
- _sb.AppendLine($"{body}if (value is {t} unwrapped) return new {name}(unwrapped);");
- _sb.AppendLine($"{body}if (value is string text)");
- _sb.AppendLine($"{body}{{");
- _sb.AppendLine($"{body} var converted = global::System.ComponentModel.TypeDescriptor.GetConverter(typeof({t})).ConvertFromString(context, culture, text);");
- _sb.AppendLine($"{body} if (converted is null) return null;");
- _sb.AppendLine($"{body} return new {name}(({t})converted);");
- _sb.AppendLine($"{body}}}");
+ if (aliasedIsString)
+ {
+ _sb.AppendLine($"{body}if (value is string unwrapped) return new {name}(unwrapped);");
+ }
+ else
+ {
+ _sb.AppendLine($"{body}if (value is {t} unwrapped) return new {name}(unwrapped);");
+ _sb.AppendLine($"{body}if (value is string text)");
+ _sb.AppendLine($"{body}{{");
+ _sb.AppendLine($"{body} var converted = _inner.ConvertFromString(context, culture, text);");
+ _sb.AppendLine($"{body} if (converted is null) return null;");
+ _sb.AppendLine($"{body} return new {name}(({t})converted);");
+ _sb.AppendLine($"{body}}}");
+ }
_sb.AppendLine($"{body}return base.ConvertFrom(context, culture, value);");
_sb.AppendLine($"{inner}}}");
_sb.AppendLine();
@@ -781,8 +802,15 @@ private void AppendSerializationConverters()
_sb.AppendLine($"{inner}{{");
_sb.AppendLine($"{body}if (value is {name} wrapped)");
_sb.AppendLine($"{body}{{");
- _sb.AppendLine($"{body} if (destinationType == typeof({t})) return wrapped._value;");
- _sb.AppendLine($"{body} if (destinationType == typeof(string)) return global::System.ComponentModel.TypeDescriptor.GetConverter(typeof({t})).ConvertToString(context, culture, wrapped._value);");
+ if (aliasedIsString)
+ {
+ _sb.AppendLine($"{body} if (destinationType == typeof(string)) return wrapped._value;");
+ }
+ else
+ {
+ _sb.AppendLine($"{body} if (destinationType == typeof({t})) return wrapped._value;");
+ _sb.AppendLine($"{body} if (destinationType == typeof(string)) return _inner.ConvertToString(context, culture, wrapped._value);");
+ }
_sb.AppendLine($"{body}}}");
_sb.AppendLine($"{body}return base.ConvertTo(context, culture, value, destinationType);");
_sb.AppendLine($"{inner}}}");
diff --git a/NewType.Generator/AliasModelExtractor.cs b/NewType.Generator/AliasModelExtractor.cs
index 9fb89bd..6aeecea 100644
--- a/NewType.Generator/AliasModelExtractor.cs
+++ b/NewType.Generator/AliasModelExtractor.cs
@@ -370,7 +370,7 @@ private static ConstraintModel ExtractValidationMethod(ITypeSymbol targetType,
foreach (var method in targetType.GetMembers().OfType())
{
- foreach (var attributeData in method.GetAttributes().Where(x => x is not null))
+ foreach (var attributeData in method.GetAttributes())
{
if (attributeData.AttributeClass!.Name == ConstraintAttributeSource.AttributeName)
{
@@ -470,14 +470,12 @@ private static bool ImplementsInterface(ITypeSymbol type, string interfaceFullNa
}
private static LocationInfo? ToLocationStruct(Location? location) =>
- location is not null && location.IsInSource ?
- new LocationInfo(
+ location is not null && location.IsInSource
+ ? new LocationInfo(
location.SourceTree.FilePath,
- location.SourceSpan,
- new LinePositionSpan(
- location.GetLineSpan().StartLinePosition,
- location.GetLineSpan().EndLinePosition))
- :null;
+ location.SourceSpan,
+ location.GetLineSpan().Span)
+ : null;
}
internal record ConstraintModel(
diff --git a/NewType.Generator/AnalyzerReleases.Unshipped.md b/NewType.Generator/AnalyzerReleases.Unshipped.md
index 20b0ef7..e2b4aa2 100644
--- a/NewType.Generator/AnalyzerReleases.Unshipped.md
+++ b/NewType.Generator/AnalyzerReleases.Unshipped.md
@@ -1,9 +1,9 @@
-; Unshipped analyzer release
+; Unshipped analyzer release
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
### New Rules
-| Rule ID | Category | Severity | Notes |
-|------------|-----------|----------|----------------|
-| NEWTYPE001 | `Unknown` | Error | AliasGenerator |
-| NEWTYPE002 | `Unknown` | Error | AliasGenerator |
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
+NEWTYPE001 | Unknown | Error | AliasGenerator
+NEWTYPE002 | Unknown | Error | AliasGenerator