diff --git a/InterfaceStubGenerator.Shared/Parser.cs b/InterfaceStubGenerator.Shared/Parser.cs index ebd7e6dee..c7e8570a8 100644 --- a/InterfaceStubGenerator.Shared/Parser.cs +++ b/InterfaceStubGenerator.Shared/Parser.cs @@ -1,5 +1,6 @@ using System.Collections.Immutable; using System.Text; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -304,9 +305,18 @@ bool nullableEnabled var derivedRefitMethods = derivedMethods .Where(m => IsRefitMethod(m, httpMethodBaseAttributeSymbol)) .ToArray(); + var derivedNonRefitMethods = derivedMethods .Except(derivedRefitMethods, SymbolEqualityComparer.Default) .Cast() + .Where(m => + // Only flag methods from interfaces that have at least one Refit method. + // Methods from plain non-Refit interfaces (e.g. IBaseApi with GetBaseUri()) + // should be silently ignored — they are not HTTP endpoints. + m.ContainingType.GetMembers() + .OfType() + .Any(im => IsRefitMethod(im, httpMethodBaseAttributeSymbol)) + ) .ToArray(); // Exclude base interface methods that the current interface explicitly implements. diff --git a/Refit.GeneratorTests/_snapshots/InterfaceTests.RefitInterfaceDerivedFromBaseTest#IGeneratedInterface.g.verified.cs b/Refit.GeneratorTests/_snapshots/InterfaceTests.RefitInterfaceDerivedFromBaseTest#IGeneratedInterface.g.verified.cs index a5fa1d22a..e17ad6243 100644 --- a/Refit.GeneratorTests/_snapshots/InterfaceTests.RefitInterfaceDerivedFromBaseTest#IGeneratedInterface.g.verified.cs +++ b/Refit.GeneratorTests/_snapshots/InterfaceTests.RefitInterfaceDerivedFromBaseTest#IGeneratedInterface.g.verified.cs @@ -37,11 +37,6 @@ public RefitGeneratorTestIGeneratedInterface(global::System.Net.Http.HttpClient return await ((global::System.Threading.Tasks.Task)______func(this.Client, ______arguments)).ConfigureAwait(false); } - /// - void global::RefitGeneratorTest.IBaseInterface.NonRefitMethod() - { - throw new global::System.NotImplementedException("Either this method has no Refit HTTP method attribute or you've used something other than a string literal for the 'path' argument."); - } } } } diff --git a/Refit.Tests/IInterfaceInheritingNonRefit.cs b/Refit.Tests/IInterfaceInheritingNonRefit.cs new file mode 100644 index 000000000..aafef5e95 --- /dev/null +++ b/Refit.Tests/IInterfaceInheritingNonRefit.cs @@ -0,0 +1,13 @@ +namespace Refit.Tests +{ + public interface IBaseApi + { + string GetBaseUri(); + } + + public interface IMyRefitApi : IBaseApi + { + [Get("/users")] + Task> GetUsers(); + } +} diff --git a/Refit.Tests/InterfaceStubGenerator.cs b/Refit.Tests/InterfaceStubGenerator.cs index 44d497c9b..4c34bfde0 100644 --- a/Refit.Tests/InterfaceStubGenerator.cs +++ b/Refit.Tests/InterfaceStubGenerator.cs @@ -147,6 +147,13 @@ public async Task GenerateInterfaceStubsWithoutNamespaceSmokeTest() var path = IntegrationTestHelper.GetPath("IServiceWithoutNamespace.cs"); await VerifyGenerator(path); } + + [Fact] + public async Task InheritingFromNonRefitInterfaceDoesNotGenerateDiagnostic() + { + var path = IntegrationTestHelper.GetPath("IInterfaceInheritingNonRefit.cs"); + await VerifyGenerator(path); + } } public static class ThisIsDumbButMightHappen @@ -210,3 +217,4 @@ Task PostMessage([Body] T message, U param1, V param2) } public interface IMessage; + diff --git a/Refit.Tests/Refit.Tests.csproj b/Refit.Tests/Refit.Tests.csproj index 105c4dd38..da6d5ba4f 100644 --- a/Refit.Tests/Refit.Tests.csproj +++ b/Refit.Tests/Refit.Tests.csproj @@ -70,4 +70,10 @@ + + + + + + diff --git a/Refit.Tests/_snapshots/InterfaceStubGeneratorTests.InheritingFromNonRefitInterfaceDoesNotGenerateDiagnostic#Generated.g.verified.cs b/Refit.Tests/_snapshots/InterfaceStubGeneratorTests.InheritingFromNonRefitInterfaceDoesNotGenerateDiagnostic#Generated.g.verified.cs new file mode 100644 index 000000000..4470d5d71 --- /dev/null +++ b/Refit.Tests/_snapshots/InterfaceStubGeneratorTests.InheritingFromNonRefitInterfaceDoesNotGenerateDiagnostic#Generated.g.verified.cs @@ -0,0 +1,24 @@ +//HintName: Generated.g.cs + +#pragma warning disable +namespace Refit.Implementation +{ + + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.Diagnostics.DebuggerNonUserCode] + [global::RefitInternalGenerated.PreserveAttribute] + [global::System.Reflection.Obfuscation(Exclude=true)] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal static partial class Generated + { +#if NET5_0_OR_GREATER + [System.Runtime.CompilerServices.ModuleInitializer] + [System.Diagnostics.CodeAnalysis.DynamicDependency(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.All, typeof(global::Refit.Implementation.Generated))] + public static void Initialize() + { + } +#endif + } +} +#pragma warning restore diff --git a/Refit.Tests/_snapshots/InterfaceStubGeneratorTests.InheritingFromNonRefitInterfaceDoesNotGenerateDiagnostic#IMyRefitApi.g.verified.cs b/Refit.Tests/_snapshots/InterfaceStubGeneratorTests.InheritingFromNonRefitInterfaceDoesNotGenerateDiagnostic#IMyRefitApi.g.verified.cs new file mode 100644 index 000000000..5519e10e3 --- /dev/null +++ b/Refit.Tests/_snapshots/InterfaceStubGeneratorTests.InheritingFromNonRefitInterfaceDoesNotGenerateDiagnostic#IMyRefitApi.g.verified.cs @@ -0,0 +1,43 @@ +//HintName: IMyRefitApi.g.cs +#nullable disable +#pragma warning disable +namespace Refit.Implementation +{ + + partial class Generated + { + + /// + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.Diagnostics.DebuggerNonUserCode] + [global::RefitInternalGenerated.PreserveAttribute] + [global::System.Reflection.Obfuscation(Exclude=true)] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + partial class RefitTestsIMyRefitApi + : global::Refit.Tests.IMyRefitApi + { + /// + public global::System.Net.Http.HttpClient Client { get; } + readonly global::Refit.IRequestBuilder requestBuilder; + + /// + public RefitTestsIMyRefitApi(global::System.Net.Http.HttpClient client, global::Refit.IRequestBuilder requestBuilder) + { + Client = client; + this.requestBuilder = requestBuilder; + } + + + /// + public async Task> GetUsers() + { + var ______arguments = global::System.Array.Empty(); + var ______func = requestBuilder.BuildRestResultFuncForMethod("GetUsers", global::System.Array.Empty() ); + + return await ((Task>)______func(this.Client, ______arguments)).ConfigureAwait(false); + } + } + } +} + +#pragma warning restore diff --git a/Refit.Tests/_snapshots/InterfaceStubGeneratorTests.InheritingFromNonRefitInterfaceDoesNotGenerateDiagnostic#PreserveAttribute.g.verified.cs b/Refit.Tests/_snapshots/InterfaceStubGeneratorTests.InheritingFromNonRefitInterfaceDoesNotGenerateDiagnostic#PreserveAttribute.g.verified.cs new file mode 100644 index 000000000..71b34929f --- /dev/null +++ b/Refit.Tests/_snapshots/InterfaceStubGeneratorTests.InheritingFromNonRefitInterfaceDoesNotGenerateDiagnostic#PreserveAttribute.g.verified.cs @@ -0,0 +1,19 @@ +//HintName: PreserveAttribute.g.cs + +#pragma warning disable +namespace RefitInternalGenerated +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.AttributeUsage (global::System.AttributeTargets.Class | global::System.AttributeTargets.Struct | global::System.AttributeTargets.Enum | global::System.AttributeTargets.Constructor | global::System.AttributeTargets.Method | global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Event | global::System.AttributeTargets.Interface | global::System.AttributeTargets.Delegate)] + sealed class PreserveAttribute : global::System.Attribute + { + // + // Fields + // + public bool AllMembers; + + public bool Conditional; + } +} +#pragma warning restore