Refit: [BUG] Using nullable reference types produces invalid C# (CS8632, CS8639)

Created on 1 Sep 2019  路  6Comments  路  Source: reactiveui/refit

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

  • Open a project containing Refit where the project targets at least C# 8.0.
  • Enable nullable reference types (e.g. setting <Nullable>enable</Nullable> in the .csproj file).
  • Add a nullable reference type annotation to one or more parameters on a Refit interface method.
  • Compile the application.

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.

bug outdated

All 6 comments

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.

Was this page helpful?
0 / 5 - 0 ratings