Skip to content

Interceptors should not internally identify calls using paths #76341

@RikkiGibson

Description

@RikkiGibson

Version Used: f8bc4f5

Steps to Reproduce:

Run the following unit test on Windows:
[Fact]
public void SignatureMismatch_08()
{
    // nint/IntPtr difference
    var source = """
        using System;

        class C
        {

            public void Method1(nint param1) => throw null!;
            public void Method2(IntPtr param1) => throw null!;
        }

        static class Program
        {
            public static void Main()
            {
                var c = new C();
                c.Method1(default!);
                c.Method2(default!);

                c.Method2(default!);
                c.Method1(default!);
            }
        }
        """;
    var locations = GetInterceptableLocations(source);
    var interceptors = $$"""
        using System.Runtime.CompilerServices;
        using System;

        static class D
        {
            [InterceptsLocation({{GetAttributeArgs(locations[0]!)}})] // 1
            [InterceptsLocation({{GetAttributeArgs(locations[1]!)}})]
            public static void Interceptor1(this C s, IntPtr param2) => Console.Write(1);

            [InterceptsLocation({{GetAttributeArgs(locations[2]!)}})] // 2
            [InterceptsLocation({{GetAttributeArgs(locations[3]!)}})]
            public static void Interceptor2(this C s, nint param2) => Console.Write(2);
        }
        """;

    // TODO2: 'InterceptableLocation' is not finding the correct call with this specific set of inputs. change ANYTHING about the content of 'source' and expected errors will be reported.
    // It doesn't appear to be a hash collision issue, unsure yet what it really is.

    var comp = CreateCompilation([source, interceptors, s_attributesSource], parseOptions: RegularWithInterceptors);
    comp.VerifyEmitDiagnostics(
        // (6,6): warning CS9154: Intercepting a call to 'C.Method1(nint)' with interceptor 'D.Interceptor1(C, IntPtr)', but the signatures do not match.
        //     [InterceptsLocation(1, "7SN+G7Nd46k5We+z0k74ffUAAAA=")] // 1
        Diagnostic(ErrorCode.WRN_InterceptorSignatureMismatch, "InterceptsLocation").WithArguments("C.Method1(nint)", "D.Interceptor1(C, System.IntPtr)").WithLocation(6, 6),
        // (7,6): error CS9144: Cannot intercept method 'Console.Write(int)' with interceptor 'D.Interceptor1(C, IntPtr)' because the signatures do not match.
        //     [InterceptsLocation(1, "7SN+G7Nd46k5We+z0k74fRMBAAA=")]
        Diagnostic(ErrorCode.ERR_InterceptorSignatureMismatch, "InterceptsLocation").WithArguments("System.Console.Write(int)", "D.Interceptor1(C, System.IntPtr)").WithLocation(7, 6),
        // (10,6): warning CS9154: Intercepting a call to 'C.Method2(IntPtr)' with interceptor 'D.Interceptor2(C, nint)', but the signatures do not match.
        //     [InterceptsLocation(1, "7SN+G7Nd46k5We+z0k74fTMBAAA=")] // 2
        Diagnostic(ErrorCode.WRN_InterceptorSignatureMismatch, "InterceptsLocation").WithArguments("C.Method2(System.IntPtr)", "D.Interceptor2(C, nint)").WithLocation(10, 6));

    // TODO2: re-enable
#if false
    var verifier = CompileAndVerify([source, interceptors, s_attributesSource], parseOptions: RegularWithInterceptors, expectedOutput: "1122");
    verifier.VerifyDiagnostics(
        // (6,6): warning CS9154: Intercepting a call to 'C.Method1(nint)' with interceptor 'D.Interceptor1(C, IntPtr)', but the signatures do not match.
        //     [InterceptsLocation(1, "3ONc0QK7vNwCujpTORn5YvcAAAA=")] // 1
        Diagnostic(ErrorCode.WRN_InterceptorSignatureMismatch, "InterceptsLocation").WithArguments("C.Method1(nint)", "D.Interceptor1(C, System.IntPtr)").WithLocation(6, 6),
        // (10,6): warning CS9154: Intercepting a call to 'C.Method2(IntPtr)' with interceptor 'D.Interceptor2(C, nint)', but the signatures do not match.
        //     [InterceptsLocation(1, "3ONc0QK7vNwCujpTORn5YjUBAAA=")] // 2
        Diagnostic(ErrorCode.WRN_InterceptorSignatureMismatch, "InterceptsLocation").WithArguments("C.Method2(System.IntPtr)", "D.Interceptor2(C, nint)").WithLocation(10, 6));
#endif
}

Remarks:

// NB: the 'Many' case for these dictionary values means there are duplicates. An error is reported for this after binding.
private ConcurrentDictionary<(string FilePath, int Position), OneOrMany<(Location AttributeLocation, MethodSymbol Interceptor)>>? _interceptions;

The problem here is that _interceptions is simply keyed off a pair of FilePath, Position. It should be keyed off something like Checksum, Position.

The impact of this bug is that compilations with duplicate file paths, or empty file paths (like most compiler tests), will effectively only identify interceptions based on position, which is quite prone to collision.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions