Describe the bug
If the new C# 8.0 nullable feature is enabled and used for any parameters on a Refit interface, invalid C# is generated which does not compile.
Steps To Reproduce
<Nullable>enable</Nullable> in the .csproj file).For example, the following interface:
public interface ITflClient
{
[Get("/Line/Mode/{supportedModes}")]
Task<ApiResponse<ICollection<LineInfo>>> GetLinesAsync(
string supportedModes,
[AliasAs("app_id")] string? applicationId,
[AliasAs("app_key")] string? applicationKey,
CancellationToken cancellationToken);
[Get("/Line/{lineId}/StopPoints")]
Task<ApiResponse<ICollection<StopPoint>>> GetStopPointsAsync(
string lineId,
[AliasAs("app_id")] string? applicationId,
[AliasAs("app_key")] string? applicationKey,
CancellationToken cancellationToken);
}
Generates the following 12 errors:
Severity Code Description Project File Line Suppression State
Error CS8632 The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. LondonTravel.Site C:\Coding\alexa-london-travel-site\src\LondonTravel.Site\obj\Debug\netcoreapp3.0\RefitStubs.g.cs 56 Active
Error CS8632 The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. LondonTravel.Site C:\Coding\alexa-london-travel-site\src\LondonTravel.Site\obj\Debug\netcoreapp3.0\RefitStubs.g.cs 56 Active
Error CS8632 The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. LondonTravel.Site C:\Coding\alexa-london-travel-site\src\LondonTravel.Site\obj\Debug\netcoreapp3.0\RefitStubs.g.cs 64 Active
Error CS8632 The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. LondonTravel.Site C:\Coding\alexa-london-travel-site\src\LondonTravel.Site\obj\Debug\netcoreapp3.0\RefitStubs.g.cs 64 Active
Error CS8639 The typeof operator cannot be used on a nullable reference type LondonTravel.Site C:\Coding\alexa-london-travel-site\src\LondonTravel.Site\obj\Debug\netcoreapp3.0\RefitStubs.g.cs 59 Active
Error CS8632 The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. LondonTravel.Site C:\Coding\alexa-london-travel-site\src\LondonTravel.Site\obj\Debug\netcoreapp3.0\RefitStubs.g.cs 59 Active
Error CS8639 The typeof operator cannot be used on a nullable reference type LondonTravel.Site C:\Coding\alexa-london-travel-site\src\LondonTravel.Site\obj\Debug\netcoreapp3.0\RefitStubs.g.cs 59 Active
Error CS8632 The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. LondonTravel.Site C:\Coding\alexa-london-travel-site\src\LondonTravel.Site\obj\Debug\netcoreapp3.0\RefitStubs.g.cs 59 Active
Error CS8639 The typeof operator cannot be used on a nullable reference type LondonTravel.Site C:\Coding\alexa-london-travel-site\src\LondonTravel.Site\obj\Debug\netcoreapp3.0\RefitStubs.g.cs 67 Active
Error CS8632 The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. LondonTravel.Site C:\Coding\alexa-london-travel-site\src\LondonTravel.Site\obj\Debug\netcoreapp3.0\RefitStubs.g.cs 67 Active
Error CS8639 The typeof operator cannot be used on a nullable reference type LondonTravel.Site C:\Coding\alexa-london-travel-site\src\LondonTravel.Site\obj\Debug\netcoreapp3.0\RefitStubs.g.cs 67 Active
Error CS8632 The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. LondonTravel.Site C:\Coding\alexa-london-travel-site\src\LondonTravel.Site\obj\Debug\netcoreapp3.0\RefitStubs.g.cs 67 Active
The generated C# in RefitStubs.g.cs is:
// <auto-generated />
using System;
using System.Net.Http;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Refit;
/* ******** Hey You! *********
*
* This is a generated file, and gets rewritten every time you build the
* project. If you want to edit it, you need to edit the mustache template
* in the Refit package */
#pragma warning disable
namespace MartinCostello.LondonTravel.Site.RefitInternalGenerated
{
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[AttributeUsage (AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate)]
sealed class PreserveAttribute : Attribute
{
//
// Fields
//
public bool AllMembers;
public bool Conditional;
}
}
#pragma warning restore
namespace MartinCostello.LondonTravel.Site.Services.Tfl
{
using MartinCostello.LondonTravel.Site.RefitInternalGenerated;
/// <inheritdoc />
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
[global::System.Diagnostics.DebuggerNonUserCode]
[Preserve]
[global::System.Reflection.Obfuscation(Exclude=true)]
partial class AutoGeneratedITflClient : ITflClient
{
/// <inheritdoc />
public HttpClient Client { get; protected set; }
readonly IRequestBuilder requestBuilder;
/// <inheritdoc />
public AutoGeneratedITflClient(HttpClient client, IRequestBuilder requestBuilder)
{
Client = client;
this.requestBuilder = requestBuilder;
}
/// <inheritdoc />
Task<ApiResponse<ICollection<LineInfo>>> ITflClient.GetLinesAsync(string supportedModes, string? applicationId, string? applicationKey, CancellationToken cancellationToken)
{
var arguments = new object[] { supportedModes, applicationId, applicationKey, cancellationToken };
var func = requestBuilder.BuildRestResultFuncForMethod("GetLinesAsync", new Type[] { typeof(string), typeof(string?), typeof(string?), typeof(CancellationToken) });
return (Task<ApiResponse<ICollection<LineInfo>>>)func(Client, arguments);
}
/// <inheritdoc />
Task<ApiResponse<ICollection<StopPoint>>> ITflClient.GetStopPointsAsync(string lineId, string? applicationId, string? applicationKey, CancellationToken cancellationToken)
{
var arguments = new object[] { lineId, applicationId, applicationKey, cancellationToken };
var func = requestBuilder.BuildRestResultFuncForMethod("GetStopPointsAsync", new Type[] { typeof(string), typeof(string?), typeof(string?), typeof(CancellationToken) });
return (Task<ApiResponse<ICollection<StopPoint>>>)func(Client, arguments);
}
}
}
The compiler errors are caused by the following code being generated: typeof(string?)
Expected behavior
The application compiles.
The error is caused by these two:
typeof(Enum?)
typeof(String?)
Any update? @joelweiss What's the status of your PR?
I couldn't figure out a solution for the remaining problems.
I was thinking of another solution by adding#nulleable enableto all generated stubs behind a compilation symbol
#if ADD_NULLABLE_ANOTATIONS
#nullable enable
#endif
and if we want to use nullable references we will have to add
#define ADD_NULLABLE_ANOTATIONS
What do you think about this approach?
How about we don't use nullable style in generated code at all?
@joelweiss
After fixed the typeof(string?) bug, we can #nullable enable annotations for all generated code. or we can suppress CS8669 warning for all generated code.
I prefer just suppress warning, since it changes nothing.
See this change based on your PR: https://github.com/joelweiss/refit/pull/1
@iron9light
nullable enable annotations for all generated code.
will probably cause a problem for older compilers.
Like you suggested let's try with just suppressing CS8669. I will try it out.