Skip to content

[Visual Studio] [C# 14] Private extension methods incorrectly marked as unused members #81657

@vsfeedback

Description

@vsfeedback

This issue has been moved from a ticket on Developer Community.


[severity:It bothers me. A fix would be nice]
When using the new C# 14 extension method syntax, Visual Studio 18.0.2 marks private extension methods as "unused members" but only if it is not called using the extension method signature.
Surely this is the side-effect of some compiler lowering to old-style extension syntax, but the IDE suggestion is just wrong and would lead to a compiler error.

Example:

public static class NewStyleExtensions
{
    extension(string input)
    {
        public T[] SplitToArray<T>(char separator) where T : IConvertible
            => [.. input.Split(separator).Select(ConvertTo<T>)];

        private T ConvertTo<T>() where T : IConvertible   // <--- This method gets wrongfully marked as "unused member"
            => (T)Convert.ChangeType(input, typeof(T));
    }
}

Note that changing the call from the public method affects this behavior:

// Alternative 1
// When called with the true extension method signature, the private method is now correctly marked as "used".
public T[] SplitToArray<T>(char separator) where T : IConvertible
    => [.. input.Split(separator).Select(x => x.ConvertTo<T>())];

// Alternative 2
// When called with the target as an input parameter, the private method is incorrectly marked as "unused" and a refactor is suggested to remove the lambda expression (to original example above).
public T[] SplitToArray<T>(char separator) where T : IConvertible
    => [.. input.Split(separator).Select(x => ConvertTo<T>(x))]; 

Original Comments

Benjamin Copass on 12/11/2025, 01:27 AM:

Another example / use case:

internal static class PrivateExtensionPropertyTest
{
    extension(object o)
    {
        private bool Foo => o is null; // faded out in Visual Studio (VS thinks it's an unused member)
    }

public static void Test()
{
object o = new object();
bool b = o.Foo; // Foo is actually used
}
}

Feedback Bot on 12/11/2025, 06:50 AM:

We have directed your feedback to the appropriate engineering team for further evaluation. The team will review the feedback and notify you about the next steps.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions