-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Handle potential null in extension lookup #80621
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 && | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
@@ -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); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
| { | ||
|
|
@@ -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; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| this.Symbol = symbol; | ||
| this.Error = error; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
| { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One such "remove without any errors" scenario is when a There are other scenarios in which lookup can return a result with a
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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) | ||
| ); | ||
| } | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider reverting change on this line. #Closed
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.