Fix support for Nullable (language version 8) (#25)

* Only emit #nullable when nullable is supported (>= 8.0)

* x

* is not
This commit is contained in:
Stef Heyenrath
2021-08-06 17:09:01 +02:00
committed by GitHub
parent 8d43c73816
commit 9dd2221f23
10 changed files with 56 additions and 34 deletions
@@ -1,24 +1,30 @@
using Microsoft.CodeAnalysis;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
namespace ProxyInterfaceSourceGenerator.Extensions
{
internal static class NamedTypeSymbolExtensions
{
/// <summary>
/// See https://www.codeproject.com/Articles/861548/Roslyn-Code-Analysis-in-Easy-Samples-Part
/// </summary>
public static string GetFullTypeString(this INamedTypeSymbol namedTypeSymbol)
public static string GetFileName(this INamedTypeSymbol namedTypeSymbol)
{
var str = new StringBuilder(namedTypeSymbol.Name);
var typeName = namedTypeSymbol.GetFullType();
return !(typeName.Contains('<') && typeName.Contains('>')) ?
typeName :
$"{typeName.Replace('.', '_').Replace('<', '_').Replace('>', '_').Replace(", ", "-")}_{typeName.Count(c => c == ',') + 1}";
}
if (namedTypeSymbol.TypeArguments.Count() > 0)
{
str.AppendFormat("<{0}>", string.Join(", ", namedTypeSymbol.TypeArguments.OfType<INamedTypeSymbol>().Select(typeArg => typeArg.GetFullTypeString())));
}
public static string GetFullType(this INamedTypeSymbol namedTypeSymbol)
{
// https://www.codeproject.com/Articles/861548/Roslyn-Code-Analysis-in-Easy-Samples-Part
//var str = new StringBuilder(namedTypeSymbol.Name);
return str.ToString();
//if (namedTypeSymbol.TypeArguments.Count() > 0)
//{
// str.AppendFormat("<{0}>", string.Join(", ", namedTypeSymbol.TypeArguments.OfType<INamedTypeSymbol>().Select(typeArg => typeArg.GetFullType())));
//}
return namedTypeSymbol.OriginalDefinition.ToString();// str.ToString();
}
public static string ResolveInterfaceNameWithOptionalTypeConstraints(this INamedTypeSymbol namedTypeSymbol, string interfaceName)
@@ -27,7 +27,7 @@ namespace ProxyInterfaceSourceGenerator.Extensions
constraints.Add("new()");
}
constraints.AddRange(typeParameterSymbol.ConstraintTypes.OfType<INamedTypeSymbol>().Select(contstraintType => contstraintType.GetFullTypeString()));
constraints.AddRange(typeParameterSymbol.ConstraintTypes.OfType<INamedTypeSymbol>().Select(contstraintType => contstraintType.GetFullType()));
if (!constraints.Any())
{
@@ -8,10 +8,12 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators
internal abstract class BaseGenerator
{
protected readonly Context _context;
protected readonly bool _supportsNullable;
public BaseGenerator(Context context)
public BaseGenerator(Context context, bool supportsNullable)
{
_context = context;
_supportsNullable = supportsNullable;
}
protected string GetPropertyType(IPropertySymbol property, out bool isReplaced)
@@ -11,8 +11,8 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators
{
internal class PartialInterfacesGenerator : BaseGenerator, IFilesGenerator
{
public PartialInterfacesGenerator(Context context) :
base(context)
public PartialInterfacesGenerator(Context context, bool supportsNullable) :
base(context, supportsNullable)
{
}
@@ -30,7 +30,7 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators
var interfaceName = targetClassSymbol.ResolveInterfaceNameWithOptionalTypeConstraints(pd.InterfaceName);
var file = new FileData(
$"{pd.FileName}.g.cs",
$"{targetClassSymbol.GetFileName()}.g.cs",
CreatePartialInterfaceCode(pd.Namespace, targetClassSymbol, interfaceName, pd.ProxyAll)
);
@@ -48,7 +48,7 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators
// </auto-generated>
//----------------------------------------------------------------------------------------
#nullable enable
{(_supportsNullable ? "#nullable enable" : string.Empty)}
using System;
namespace {ns}
@@ -62,7 +62,7 @@ namespace {ns}
{GenerateEvents(targetClassSymbol)}
}}
}}
#nullable disable";
{(_supportsNullable ? "#nullable disable" : string.Empty)}";
private string GenerateProperties(INamedTypeSymbol targetClassSymbol, bool proxyAll)
{
@@ -12,7 +12,7 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators
{
internal class ProxyClassesGenerator : BaseGenerator, IFilesGenerator
{
public ProxyClassesGenerator(Context context) : base(context)
public ProxyClassesGenerator(Context context, bool supportsNullable) : base(context, supportsNullable)
{
}
@@ -32,7 +32,7 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators
var constructorName = $"{targetClassSymbol.Name}Proxy";
var file = new FileData(
$"{pd.FileName}Proxy.g.cs",
$"{targetClassSymbol.GetFileName()}Proxy.g.cs",
CreateProxyClassCode(pd.Namespace, targetClassSymbol, interfaceName, className, constructorName)
);
@@ -41,7 +41,12 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators
return file;
}
private string CreateProxyClassCode(string ns, INamedTypeSymbol targetClassSymbol, string interfaceName, string className, string constructorName) => $@"//----------------------------------------------------------------------------------------
private string CreateProxyClassCode(
string ns,
INamedTypeSymbol targetClassSymbol,
string interfaceName,
string className,
string constructorName) => $@"//----------------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by https://github.com/StefH/ProxyInterfaceSourceGenerator.
//
@@ -50,7 +55,7 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators
// </auto-generated>
//----------------------------------------------------------------------------------------
#nullable enable
{(_supportsNullable ? "#nullable enable" : string.Empty)}
using System;
using AutoMapper;
@@ -76,7 +81,7 @@ namespace {ns}
{GeneratePrivateAutoMapper()}
}}
}}
#nullable disable";
{(_supportsNullable ? "#nullable disable" : string.Empty)}";
private string GeneratePrivateAutoMapper()
{
return _context.ReplacedTypes.Count == 0 ? string.Empty : " private readonly IMapper _mapper;";
@@ -1,5 +1,7 @@
using System;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using ProxyInterfaceSourceGenerator.FileGenerators;
using ProxyInterfaceSourceGenerator.SyntaxReceiver;
@@ -23,14 +25,22 @@ namespace ProxyInterfaceSourceGenerator
public void Execute(GeneratorExecutionContext context)
{
if (context.ParseOptions is not CSharpParseOptions csharpParseOptions)
{
throw new NotSupportedException("Only C# is supported.");
}
if (context.SyntaxReceiver is not ProxySyntaxReceiver receiver)
{
return;
}
// https://github.com/reactiveui/refit/blob/main/InterfaceStubGenerator.Core/InterfaceStubGenerator.cs
var supportsNullable = csharpParseOptions.LanguageVersion >= LanguageVersion.CSharp8;
GenerateProxyAttribute(context, receiver);
GeneratePartialInterfaces(context, receiver);
GenerateProxyClasses(context, receiver);
GeneratePartialInterfaces(context, receiver, supportsNullable);
GenerateProxyClasses(context, receiver, supportsNullable);
}
private void GenerateProxyAttribute(GeneratorExecutionContext ctx, ProxySyntaxReceiver receiver)
@@ -45,7 +55,7 @@ namespace ProxyInterfaceSourceGenerator
context.GeneratorExecutionContext.AddSource(attributeData.FileName, SourceText.From(attributeData.Text, Encoding.UTF8));
}
private static void GeneratePartialInterfaces(GeneratorExecutionContext ctx, ProxySyntaxReceiver receiver)
private static void GeneratePartialInterfaces(GeneratorExecutionContext ctx, ProxySyntaxReceiver receiver, bool supportsNullable)
{
var context = new Context
{
@@ -53,14 +63,14 @@ namespace ProxyInterfaceSourceGenerator
CandidateInterfaces = receiver.CandidateInterfaces
};
var partialInterfacesGenerator = new PartialInterfacesGenerator(context);
var partialInterfacesGenerator = new PartialInterfacesGenerator(context, supportsNullable);
foreach (var data in partialInterfacesGenerator.GenerateFiles())
{
context.GeneratorExecutionContext.AddSource(data.FileName, SourceText.From(data.Text, Encoding.UTF8));
}
}
private static void GenerateProxyClasses(GeneratorExecutionContext ctx, ProxySyntaxReceiver receiver)
private static void GenerateProxyClasses(GeneratorExecutionContext ctx, ProxySyntaxReceiver receiver, bool supportsNullable)
{
var context = new Context
{
@@ -68,7 +78,7 @@ namespace ProxyInterfaceSourceGenerator
CandidateInterfaces = receiver.CandidateInterfaces
};
var proxyClassesGenerator = new ProxyClassesGenerator(context);
var proxyClassesGenerator = new ProxyClassesGenerator(context, supportsNullable);
foreach (var data in proxyClassesGenerator.GenerateFiles())
{
context.GeneratorExecutionContext.AddSource(data.FileName, SourceText.From(data.Text, Encoding.UTF8));
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>0.0.9</Version>
<Version>0.0.10</Version>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>9</LangVersion>
<Nullable>enable</Nullable>
@@ -2,8 +2,7 @@ using System.Collections.Generic;
namespace ProxyInterfaceSourceGenerator.SyntaxReceiver
{
internal record ProxyData(string Namespace, string InterfaceName, string RawTypeName, string TypeName, List<string> Usings, bool ProxyAll)
internal record ProxyData(string Namespace, string InterfaceName, string RawTypeName, string TypeName, List<string> Usings, bool ProxyAll)
{
public string FileName => TypeName.Replace('.', '_').Replace('`', '_');
}
}