S4039 should not raise for interface default implementations

Having the following example code:

public interface IBaseInterface
{
    void Method1(string parameter);
}

public interface IDerivedInterface : IBaseInterface
{
    void Method2(int parameter);

    void IBaseInterface.Method1(string parameter) => this.Method2(Convert.ToInt32(parameter, CultureInfo.InvariantCulture)); // S4039
}

As mentioned above, this code raises an issue for rule S4039: “Interface methods should be callable by derived types”.
Why this is raised here? The interface cannot be made sealed, which makes no sense for interfaces.
Is there any alternative to implement such default implementation without triggering the rule?
I think, this is a false-positive.

Hi,

I’m sure you’ve told me in other threads, but I have the retention of a tse-tse fly for these things. :sweat_smile: Can you give your context for this? I.e. are you on SonarQube Cloud? SonarQube for IDE (flavor and version)? SonarQube self-managed (flavor and version)?

 
Thx,
Ann

1 Like

Sorry, I omit these information explicitly because I assumed that all current versions have the same problem.
But, ok. Here are the versions I’ve verified this issue:
SonarQube for IDE (Rider extension): 11.15.0.84329
SonarQube Server Developer Edition: v2025.5

1 Like

Hi,

You’re in the latest version of SonarQube for IntelliJ/Rider, but you’re connected to a slightly stale version of SonarQube (2026.1 is the current LTA and 2026.2 is the Latest.) However, I don’t see any fixed tickets in Jira for this, so I’m going to flag it for the language devs.

 
Ann

Hello @lg2de

Hi,

I believe this is actually a true positive — the rule is
correctly identifying the issue for which it was made.

Here’s why the rule raises: when IDerivedInterface provides an explicit default implementation of IBaseInterface.Method1, any class implementing IDerivedInterface cannot call Method1 directly. One would need need to cast
((IBaseInterface)this).Method1(…). Moreover, if a class later re-implements IBaseInterface.Method1 explicitly, the
IDerivedInterface default becomes completely inaccessible, there’s no way to call it from an implementing type. This is exactly the accessibility concern S4039 is designed to flag.

However, the diagnostic message is confusing in this context and it needs improvement.
I will open a backlog ticket for this!

Not only the message is confusing. I cannot change the derived interface to fulfill the rule.
It should never raise on interfaces itself.

Hello again @lg2de

Could you please give me some more context on how this design is used?
Is the goal to hide the method because you don’t need it when implementing IDerivedInterface?
Some other pattern?

Found the pattern - interfaces should be excluded.

Please still confirm me if the point is that you want the method hidden from the derived class but still available in case an object of the derived class is passed as `IBaseInterface`.

In short: I can confirm that.

But I’d like to elaborate a bit further: I don’t think we have any other choice but to see it this way, unless the C# specification is changed.
I’m assuming here that the existence of IBaseInterface and IDerivedInterface is not in question in this example. If that is the case, then the behavior of IDerivedInterface is now defined such that it predefines the method IBaseInterface.Method1 for all derived classes.
Derived classes cannot directly access this override. Nor should they have to, because that is the intention of IDerivedInterface.

Thank you for the confirmation.
I have added this case to our backlog as an FP (it’s internal only unfortunately).
Best
Mary