diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs index 67e36528c8749..b106c8e0509f8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs @@ -199,30 +199,34 @@ private void LookupAllExtensionMembersInSingleBinder(LookupResult result, string internal void EnumerateAllExtensionMembersInSingleBinder(ArrayBuilder result, string? name, int arity, LookupOptions options, Binder originalBinder, ref CompoundUseSiteInfo useSiteInfo, ref CompoundUseSiteInfo classicExtensionUseSiteInfo) { - // 1. Collect new extension members PooledHashSet? implementationsToShadow = null; - var extensionDeclarations = ArrayBuilder.GetInstance(); - this.GetExtensionDeclarations(extensionDeclarations, originalBinder); - foreach (NamedTypeSymbol extensionDeclaration in extensionDeclarations) + // 1. Collect new extension members + if (this.Compilation.LanguageVersion.AllowNewExtensions()) { - var candidates = name is null ? extensionDeclaration.GetMembers() : extensionDeclaration.GetMembers(name); + var extensionDeclarations = ArrayBuilder.GetInstance(); + this.GetExtensionDeclarations(extensionDeclarations, originalBinder); - foreach (var candidate in candidates) + foreach (NamedTypeSymbol extensionDeclaration in extensionDeclarations) { - SingleLookupResult resultOfThisMember = originalBinder.CheckViability(candidate, arity, options, null, diagnose: true, useSiteInfo: ref useSiteInfo); - result.Add(resultOfThisMember); + var candidates = name is null ? extensionDeclaration.GetMembers() : extensionDeclaration.GetMembers(name); - if (candidate is MethodSymbol { IsStatic: false } shadows && - shadows.OriginalDefinition.TryGetCorrespondingExtensionImplementationMethod() is { } toShadow) + foreach (var candidate in candidates) { - implementationsToShadow ??= PooledHashSet.GetInstance(); - implementationsToShadow.Add(toShadow); + SingleLookupResult resultOfThisMember = originalBinder.CheckViability(candidate, arity, options, null, diagnose: true, useSiteInfo: ref useSiteInfo); + result.Add(resultOfThisMember); + + if (candidate is MethodSymbol { IsStatic: false } shadows && + shadows.OriginalDefinition.TryGetCorrespondingExtensionImplementationMethod() is { } toShadow) + { + implementationsToShadow ??= PooledHashSet.GetInstance(); + implementationsToShadow.Add(toShadow); + } } } - } - extensionDeclarations.Free(); + extensionDeclarations.Free(); + } // 2. Collect classic extension methods var extensionMethods = ArrayBuilder.GetInstance(); diff --git a/src/Compilers/CSharp/Portable/LanguageVersion.cs b/src/Compilers/CSharp/Portable/LanguageVersion.cs index 4d429582030ef..ec5d56afa9904 100644 --- a/src/Compilers/CSharp/Portable/LanguageVersion.cs +++ b/src/Compilers/CSharp/Portable/LanguageVersion.cs @@ -572,5 +572,10 @@ internal static bool AllowImprovedOverloadCandidates(this LanguageVersion self) { return self >= MessageID.IDS_FeatureImprovedOverloadCandidates.RequiredVersion(); } + + internal static bool AllowNewExtensions(this LanguageVersion self) + { + return self >= MessageID.IDS_FeatureExtensions.RequiredVersion(); + } } } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs index cb681b90009bd..c342e15555a9b 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs @@ -26600,4 +26600,126 @@ void M(U u) Assert.Equal("void E.<>E__0.M(U u)", model.GetSymbolInfo(expr).Symbol.ToTestDisplayString()); Assert.Equal(["void E.<>E__0.M(U u)"], model.GetMemberGroup(expr).ToTestDisplayStrings()); } + + [Fact] + public void GetSymbolInfo_08() + { + var src = """ +public static class E +{ + public static void M(this T t) + { + t.M(); + t.M(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var extensionParameterSyntax = tree.GetRoot().DescendantNodes().OfType().First(); + IParameterSymbol extensionParameter = model.GetDeclaredSymbol(extensionParameterSyntax); + Assert.Equal("T t", extensionParameter.ToTestDisplayString()); + var t = extensionParameter.Type; + + var expr = GetSyntax(tree, "t.M()").Expression; + Assert.Equal("void T.M()", model.GetSymbolInfo(expr).Symbol.ToTestDisplayString()); + Assert.Equal(["void T.M()"], model.GetMemberGroup(expr).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates(["void T.M()"], model.LookupSymbols(position: expr.SpanStart, t, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + expr = GetSyntax(tree, "t.M()").Expression; + Assert.Equal("void T.M()", model.GetSymbolInfo(expr).Symbol.ToTestDisplayString()); + Assert.Equal(["void T.M()"], model.GetMemberGroup(expr).ToTestDisplayStrings()); + } + + [Fact] + public void LangVer_01() + { + var libSrc = """ +public static class E +{ + extension(object) + { + public void M() { } + public static void M2() { } + public static int P => 0; + } +} + +"""; + var libComp = CreateCompilation(libSrc, parseOptions: TestOptions.RegularNext); + libComp.VerifyEmitDiagnostics(); + var libRef = libComp.EmitToImageReference(); + + var srcCompat = """ +new object().M(); +System.Action a = new object().M; +var x = new object().M; + +E.M(new object()); +E.get_P(); +E.M2(); +"""; + var comp = CreateCompilation(srcCompat, references: [libRef], parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(srcCompat, references: [libRef], parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(srcCompat, references: [libRef]); + comp.VerifyEmitDiagnostics(); + + // PROTOTYPE function type not yet supported + var src = """ +object.M2(); +System.Action a = object.M2; +//var x = object.M2; + +_ = object.P; +"""; + comp = CreateCompilation(src, references: [libRef], parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M2' + // object.M2(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M2").WithArguments("object", "M2").WithLocation(1, 8), + // (2,26): error CS0117: 'object' does not contain a definition for 'M2' + // System.Action a = object.M2; + Diagnostic(ErrorCode.ERR_NoSuchMember, "M2").WithArguments("object", "M2").WithLocation(2, 26), + // (5,12): error CS0117: 'object' does not contain a definition for 'P' + // _ = object.P; + Diagnostic(ErrorCode.ERR_NoSuchMember, "P").WithArguments("object", "P").WithLocation(5, 12)); + verifySymbolInfo(comp, newLangVer: false); + + comp = CreateCompilation(src, references: [libRef], parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(); + verifySymbolInfo(comp, newLangVer: true); + + comp = CreateCompilation(src, references: [libRef]); + comp.VerifyEmitDiagnostics(); + verifySymbolInfo(comp, newLangVer: true); + + static void verifySymbolInfo(CSharpCompilation comp, bool newLangVer) + { + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); + + if (newLangVer) + { + AssertEqualAndNoDuplicates(["void E.<>E__0.M()"], model.LookupSymbols(position: 0, o, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["void E.<>E__0.M2()"], model.LookupSymbols(position: 0, o, name: "M2", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["System.Int32 E.<>E__0.P { get; }"], model.LookupSymbols(position: 0, o, name: "P", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + } + else + { + AssertEqualAndNoDuplicates(["void System.Object.M()"], model.LookupSymbols(position: 0, o, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "M2", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "P", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + } + } + } }