@@ -1972,6 +1972,9 @@ private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics)
19721972 var conversionsAsMethods = new Dictionary < MethodSymbol , SourceMemberMethodSymbol > ( MemberSignatureComparer . DuplicateSourceComparer ) ;
19731973 var conversionsAsConversions = new HashSet < SourceUserDefinedConversionSymbol > ( ConversionSignatureComparer . Comparer ) ;
19741974
1975+ Dictionary < MethodSymbol , OneOrMany < MethodSymbol > > ? staticExtensionImplementationsWithoutReturn = null ;
1976+ Dictionary < MethodSymbol , MethodSymbol > ? staticExtensionImplementationsWithReturn = null ;
1977+
19751978 // SPEC: The signature of an operator must differ from the signatures of all other
19761979 // SPEC: operators declared in the same class.
19771980
@@ -2113,11 +2116,89 @@ private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics)
21132116 {
21142117 ReportMethodSignatureCollision ( diagnostics , conversion , previousMethod ) ;
21152118 }
2119+
2120+ if ( staticExtensionImplementationsWithoutReturn ? . TryGetValue ( conversion , out OneOrMany < MethodSymbol > previousStaticExtension ) == true )
2121+ {
2122+ Debug . Assert ( false ) ; // At the moment this code path is not reachable because implementation methods always come after all user defined methods
2123+ ReportMethodSignatureCollision ( diagnostics , conversion , previousStaticExtension . First ( ) ) ;
2124+ }
2125+
21162126 // Do not add the conversion to the set of previously-seen methods; that set
21172127 // is only non-conversion methods.
21182128 }
2129+ else if ( symbol is SourceExtensionImplementationMethodSymbol { UnderlyingMethod . IsStatic : true } staticExtension )
2130+ {
2131+ staticExtensionImplementationsWithReturn ??= new Dictionary < MethodSymbol , MethodSymbol > ( MemberSignatureComparer . DuplicateSourceWithReturnComparer ) ;
2132+
2133+ // Does this method collide with any previously-seen static extension implementation?
2134+ if ( staticExtensionImplementationsWithReturn . TryGetValue ( staticExtension , out var fullMatch ) )
2135+ {
2136+ ReportMethodSignatureCollision ( diagnostics , staticExtension , fullMatch ) ;
2137+ }
2138+ else
2139+ {
2140+ staticExtensionImplementationsWithReturn . Add ( staticExtension , staticExtension ) ;
2141+
2142+ staticExtensionImplementationsWithoutReturn ??= new Dictionary < MethodSymbol , OneOrMany < MethodSymbol > > ( MemberSignatureComparer . DuplicateSourceComparer ) ;
2143+
2144+ if ( staticExtensionImplementationsWithoutReturn . TryGetValue ( staticExtension , out OneOrMany < MethodSymbol > previousStaticExtension ) )
2145+ {
2146+ NamedTypeSymbol extension = staticExtension . UnderlyingMethod . ContainingType ;
2147+ var extensionParameter = extension . ExtensionParameter ;
2148+ bool foundExtendedTypeConflict = false ;
2149+
2150+ if ( extensionParameter is not null )
2151+ {
2152+ foreach ( SourceExtensionImplementationMethodSymbol partialMatch in previousStaticExtension )
2153+ {
2154+ NamedTypeSymbol partialMatchExtension = partialMatch . UnderlyingMethod . ContainingType ;
2155+ if ( extension . Arity != partialMatchExtension . Arity ||
2156+ partialMatchExtension . ExtensionParameter is not { } partialMatchExtensionParameter )
2157+ {
2158+ continue ;
2159+ }
2160+
2161+ if ( extensionParameter . Type . Equals (
2162+ extension . Arity == 0 ?
2163+ partialMatchExtensionParameter . Type :
2164+ ( new TypeMap ( partialMatchExtension . TypeParameters , extension . TypeParameters ) ) . SubstituteType ( partialMatchExtensionParameter . Type ) . Type ,
2165+ TypeCompareKind . AllIgnoreOptions ) )
2166+ {
2167+ foundExtendedTypeConflict = true ;
2168+ ReportMethodSignatureCollision ( diagnostics , staticExtension , partialMatch ) ;
2169+ break ;
2170+ }
2171+ }
2172+ }
2173+
2174+ if ( ! foundExtendedTypeConflict )
2175+ {
2176+ staticExtensionImplementationsWithoutReturn [ staticExtension ] = previousStaticExtension . Add ( staticExtension ) ;
2177+ }
2178+ }
2179+ else
2180+ {
2181+ staticExtensionImplementationsWithoutReturn . Add ( staticExtension , OneOrMany . Create < MethodSymbol > ( staticExtension ) ) ;
2182+ }
2183+ }
2184+
2185+ // Does this static extension implementation collide *as a method* with any previously-seen
2186+ // non-static-extension-implementation method?
2187+
2188+ if ( methodsBySignature . TryGetValue ( staticExtension , out var previousMethod ) )
2189+ {
2190+ ReportMethodSignatureCollision ( diagnostics , staticExtension , previousMethod ) ;
2191+ }
2192+
2193+ if ( conversionsAsMethods . TryGetValue ( staticExtension , out var previousConversion ) )
2194+ {
2195+ ReportMethodSignatureCollision ( diagnostics , staticExtension , previousConversion ) ;
2196+ }
2197+ }
21192198 else if ( symbol is MethodSymbol method && method is ( SourceMemberMethodSymbol or SourceExtensionImplementationMethodSymbol ) )
21202199 {
2200+ Debug . Assert ( method is not SourceExtensionImplementationMethodSymbol { UnderlyingMethod . IsStatic : true } ) ;
2201+
21212202 // Does this method collide *as a method* with any previously-seen
21222203 // conversion?
21232204
@@ -2128,7 +2209,15 @@ private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics)
21282209 // Do not add the method to the set of previously-seen conversions.
21292210
21302211 // Does this method collide *as a method* with any previously-seen
2131- // non-conversion method?
2212+ // static extension implementation method?
2213+
2214+ if ( staticExtensionImplementationsWithoutReturn ? . TryGetValue ( method , out OneOrMany < MethodSymbol > previousStaticExtension ) == true )
2215+ {
2216+ ReportMethodSignatureCollision ( diagnostics , method , previousStaticExtension . First ( ) ) ;
2217+ }
2218+
2219+ // Does this method collide *as a method* with any previously-seen
2220+ // non-conversion or non-static-extension-implementation method?
21322221
21332222 if ( methodsBySignature . TryGetValue ( method , out var previousMethod ) )
21342223 {
0 commit comments