Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Crefs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ ImmutableArray<Symbol> computeSortedAndFilteredCrefExtensionMembers(NamespaceOrT

if (result.Kind == LookupResultKind.Viable)
{
Debug.Assert(result.Symbol is not null);
sortedSymbolsBuilder ??= ArrayBuilder<Symbol>.GetInstance();
sortedSymbolsBuilder.Add(result.Symbol);
}
Expand Down
6 changes: 4 additions & 2 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10896,7 +10896,8 @@ private MethodGroupResolution ResolveDefaultMethodGroup(

foreach (SingleLookupResult singleLookupResult in singleLookupResults)
{
Symbol extensionMember = singleLookupResult.Symbol;
var extensionMember = singleLookupResult.Symbol;
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var

Consider reverting change on this line. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized that it is probably driven by added nullable annotations

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that's the reason.

Debug.Assert(extensionMember is not null);
if (IsStaticInstanceMismatchForUniqueSignatureFromMethodGroup(receiver, extensionMember))
{
// Remove static/instance mismatches
Expand Down Expand Up @@ -11109,7 +11110,8 @@ private static bool IsStaticInstanceMismatchForUniqueSignatureFromMethodGroup(Bo
var methods = ArrayBuilder<MethodSymbol>.GetInstance(capacity: singleLookupResults.Count);
foreach (SingleLookupResult singleLookupResult in singleLookupResults)
{
Symbol extensionMember = singleLookupResult.Symbol;
var extensionMember = singleLookupResult.Symbol;
Debug.Assert(extensionMember is not null);
if (IsStaticInstanceMismatchForUniqueSignatureFromMethodGroup(receiver, extensionMember))
{
// Remove static/instance mismatches
Expand Down
11 changes: 10 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,12 @@ internal void EnumerateAllExtensionMembersInSingleBinder(ArrayBuilder<SingleLook
foreach (var candidate in extensionCandidates)
{
SingleLookupResult resultOfThisMember = originalBinder.CheckViability(candidate, arity, options, null, diagnose: true, useSiteInfo: ref useSiteInfo);
if (resultOfThisMember.Kind == LookupResultKind.Empty)
{
continue;
}

Debug.Assert(resultOfThisMember.Symbol is not null);
result.Add(resultOfThisMember);

if (candidate is MethodSymbol { IsStatic: false } shadows &&
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (candidate is MethodSymbol { IsStatic: false } shadows &&

It looks like this if belongs inside the if added above #Closed

Expand All @@ -230,7 +236,10 @@ internal void EnumerateAllExtensionMembersInSingleBinder(ArrayBuilder<SingleLook
if (implementationsToShadow is null || !implementationsToShadow.Remove(method.OriginalDefinition))
{
SingleLookupResult resultOfThisMember = originalBinder.CheckViability(method, arity, options, null, diagnose: true, useSiteInfo: ref classicExtensionUseSiteInfo);
result.Add(resultOfThisMember);
if (resultOfThisMember.Kind != LookupResultKind.Empty)
{
result.Add(resultOfThisMember);
}
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/Compilers/CSharp/Portable/Binder/SingleLookupResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Diagnostics;

namespace Microsoft.CodeAnalysis.CSharp
{
Expand All @@ -22,13 +21,14 @@ internal readonly struct SingleLookupResult
internal readonly LookupResultKind Kind;

// the symbol or null.
internal readonly Symbol Symbol;
internal readonly Symbol? Symbol;

// the error of the result, if it is NonViable or Inaccessible
internal readonly DiagnosticInfo Error;
internal readonly DiagnosticInfo? Error;

internal SingleLookupResult(LookupResultKind kind, Symbol symbol, DiagnosticInfo error)
internal SingleLookupResult(LookupResultKind kind, Symbol? symbol, DiagnosticInfo? error)
{
Debug.Assert(symbol is not null || kind == LookupResultKind.Empty);
this.Kind = kind;
Copy link
Contributor

@AlekseyTs AlekseyTs Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.Kind = kind;

Consider adding an assert that symbol is not null unless kind is empty. #Closed

this.Symbol = symbol;
this.Error = error;
Expand Down
92 changes: 92 additions & 0 deletions src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35085,5 +35085,97 @@ public void SyntaxFacts_01()

Assert.Equal("extension", SyntaxFacts.GetText(SyntaxKind.ExtensionKeyword));
}

[Theory, WorkItem("https://developercommunity.visualstudio.com/t/NRE-in-Roslyn-v500-225451107/10979295")]
[InlineData(LanguageVersion.CSharp10)]
[InlineData(LanguageVersion.Latest)]
public void InvalidReceiverWithOldExtensionInFileClass(LanguageVersion languageVersion)
{
var code = """
#nullable enable
using System.Threading.Tasks;
using N2;
namespace N1
{
class C
{
async Task M(object o)
{
await using var test = await nonExistent.ExtensionMethod(o);
}
}
}
""";

var code2 = """
namespace N2;

file static class E
{
Copy link
Member Author

@333fred 333fred Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One such "remove without any errors" scenario is when a file class is out of scope:

https://github.com/333fred/roslyn/blob/1a6213d6fe1b37b01bd8816f5a93d681d070e435/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs#L1471-L1475

There are other scenarios in which lookup can return a result with a null symbol, but this proved to be the easiest to replicate as @viceroypenguin gave me a dump where he hit it. #Closed

Copy link
Member

@jcouv jcouv Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this. I don't quite understand how the scenario differs from existing InstanceMethodInvocation_VariousScopes_Errors which also has a file-scoped extension (therefore inaccessible) :-/

Update: ah, the receiver is non existent...

public static void ExtensionMethod(this object o) { }
}
""";

var comp = CreateCompilation([(code, "file1.cs"), (code2, "file2.cs")], parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion));

if (languageVersion == LanguageVersion.CSharp10)
{
comp.VerifyEmitDiagnostics(
// file2.cs(3,19): error CS8936: Feature 'file types' is not available in C# 10.0. Please use language version 11.0 or greater.
// file static class E
Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion10, "E").WithArguments("file types", "11.0").WithLocation(3, 19),
// file1.cs(10,42): error CS0103: The name 'nonExistent' does not exist in the current context
// await using var test = await nonExistent.ExtensionMethod(o);
Diagnostic(ErrorCode.ERR_NameNotInContext, "nonExistent").WithArguments("nonExistent").WithLocation(10, 42)
);
}
else
{
comp.VerifyEmitDiagnostics(
// file1.cs(10,42): error CS0103: The name 'nonExistent' does not exist in the current context
// await using var test = await nonExistent.ExtensionMethod(o);
Diagnostic(ErrorCode.ERR_NameNotInContext, "nonExistent").WithArguments("nonExistent").WithLocation(10, 42)
);
}
}

[Fact, WorkItem("https://developercommunity.visualstudio.com/t/NRE-in-Roslyn-v500-225451107/10979295")]
public void InvalidReceiverWithNewExtensionInFileClass()
{
var code = """
#nullable enable
using System.Threading.Tasks;
using N2;
namespace N1
{
class C
{
async Task M(object o)
{
await using var test = await nonExistent.ExtensionMethod(o);
}
}
}
""";

var code2 = """
namespace N2;

file static class E
{
extension(object o)
{
public void ExtensionMethod(object o2) { }
}
}
""";

var comp = CreateCompilation([(code, "file1.cs"), (code2, "file2.cs")]);
comp.VerifyEmitDiagnostics(
// file1.cs(10,42): error CS0103: The name 'nonExistent' does not exist in the current context
// await using var test = await nonExistent.ExtensionMethod(o);
Diagnostic(ErrorCode.ERR_NameNotInContext, "nonExistent").WithArguments("nonExistent").WithLocation(10, 42)
);
}
}