Skip to content

Only run a single pass of NullableWalker per-member#46402

Merged
RikkiGibson merged 4 commits intodotnet:masterfrom
RikkiGibson:nullable-single-pass
Jul 30, 2020
Merged

Only run a single pass of NullableWalker per-member#46402
RikkiGibson merged 4 commits intodotnet:masterfrom
RikkiGibson:nullable-single-pass

Conversation

@RikkiGibson
Copy link
Member

@RikkiGibson RikkiGibson commented Jul 29, 2020

Closes #35041

🦔

@RikkiGibson RikkiGibson force-pushed the nullable-single-pass branch from fb82105 to e660438 Compare July 29, 2020 14:32
// once.
// https://github.com/dotnet/roslyn/issues/35041
methodBodyForSemanticModel = NullableWalker.AnalyzeAndRewrite(bodyBinder.Compilation, method, methodBody, bodyBinder, new DiagnosticBag(), createSnapshots: true, out snapshotManager, ref remappedSymbols);
case (true, { NullableSemanticAnalysisEnabled: true }):
Copy link
Member Author

Choose a reason for hiding this comment

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

We used to ignore the language version when running nullable semantic analysis. It didn't affect whether we gave diagnostics, but now it does. An alternative to this solution would be to do the analysis, passing a dummy diagnostic bag for case (false, { NullableSemanticAnalysisEnabled: true }).

We can look into potentially giving the nullability diagnostics even if the language version is insufficient, if we ShouldRunNullableWalker for example. But I did not want to take on the test churn for it in this PR.

Copy link
Member

Choose a reason for hiding this comment

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

We used to ignore the language version when running nullable semantic analysis. It didn't affect whether we gave diagnostics, but now it does.

Can we condition those diagnostics to a warning wave instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

To be clear, the "new" diagnostics would only be for an error scenario where a project has a pre-C# 8 language version, but also has NRT enabled using project-level settings or #nullable directives in source. It feels like we don't need to worry about gating such warnings behind a warning wave, because the project already won't compile.

Copy link
Contributor

Choose a reason for hiding this comment

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

We used to ignore the language version when running nullable semantic analysis. It didn't affect whether we gave diagnostics, but now it does.

I am not sure this is the behavior change that we want. It looks like this affects whether the nullability information is exposed through SemanticModel and IOperation, i.e. potentially affects behavior of analyzers and IDE features.


In reply to: 462349019 [](ancestors = 462349019)

Copy link
Contributor

Choose a reason for hiding this comment

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

I would suggest to avoid any observable behavior changes in this PR.


In reply to: 462651778 [](ancestors = 462651778,462349019)

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like we are missing a regression test that is supposed to catch this behavior change. Consider adding one.


In reply to: 462654191 [](ancestors = 462654191,462651778,462349019)

Copy link
Member Author

Choose a reason for hiding this comment

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

I would like to add the regression test, but I was surprised that the following test passed against a1474c7. Basically it seems like EnsureNullabilityAnalysisPerformedIfNecessary() will catch us and build out the nullability info even if we neglected to do it during binding. I should probably sync with someone who understands the nullable semantic model better to make sure I understand the situation properly.

        [Fact]
        public void CSharp73ProvidesNullableSemanticInfo()
        {
            var source = @"
class C
{
    void M(string s)
    {
        if (s == null)
        {
            s.ToString();
        }
    }
}
";
            var comp = CreateCompilation(source, options: WithNonNullTypesTrue(), parseOptions: TestOptions.Regular7_3, skipUsesIsNullable: true);
            comp.VerifyDiagnostics(
                // error CS8630: Invalid 'NullableContextOptions' value: 'Enable' for C# 7.3. Please use language version '8.0' or greater.
                Diagnostic(ErrorCode.ERR_NullableOptionNotAvailable).WithArguments("NullableContextOptions", "Enable", "7.3", "8.0").WithLocation(1, 1)
                );

            var tree = comp.SyntaxTrees.Single();
            var model = comp.GetSemanticModel(tree);

            var node = tree.GetRoot().DescendantNodes().OfType<MemberAccessExpressionSyntax>().Single();
            var info = model.GetTypeInfo(node.Expression);
            Assert.Equal(PublicNullableAnnotation.Annotated, info.Nullability.Annotation);
            Assert.Equal(PublicNullableFlowState.MaybeNull, info.Nullability.FlowState);
        }
        

Copy link
Contributor

Choose a reason for hiding this comment

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

I would like to add the regression test, but I was surprised that the following test passed against a1474c7. Basically it seems like EnsureNullabilityAnalysisPerformedIfNecessary() will catch us and build out the nullability info even if we neglected to do it during binding. I should probably sync with someone who understands the nullable semantic model better to make sure I understand the situation properly.

The code in method compiler doesn't affect consumers that explicitly request semantic model from a compilation. Only those that get semantic model from an analysis context in an Analyzer action are going to be affected.


In reply to: 462687379 [](ancestors = 462687379)

@RikkiGibson RikkiGibson marked this pull request as ready for review July 29, 2020 14:36
@RikkiGibson RikkiGibson requested a review from a team as a code owner July 29, 2020 14:36
@RikkiGibson
Copy link
Member Author

PTAL @333fred @dotnet/roslyn-compiler

@RikkiGibson RikkiGibson requested a review from 333fred July 29, 2020 17:56
@AlekseyTs
Copy link
Contributor

AlekseyTs commented Jul 29, 2020

Done with review pass (iteration 2) #Closed

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Jul 30, 2020

Done with review pass (iteration 3) #Closed

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

LGTM (commit 3)

@333fred
Copy link
Member

333fred commented Jul 30, 2020

Would just suggest adding an analyzer-based test for the scenario Aleksey pointed out.


Assert.Equal(PublicNullableAnnotation.Annotated, info.Nullability.Annotation);
Assert.Equal(PublicNullableFlowState.MaybeNull, info.Nullability.FlowState);

Copy link
Contributor

@AlekseyTs AlekseyTs Jul 30, 2020

Choose a reason for hiding this comment

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

Did you confirm that this assert fails for implementation from iteration 2? #Closed

Copy link
Member Author

@RikkiGibson RikkiGibson Jul 30, 2020

Choose a reason for hiding this comment

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

In the old iteration, this assert does not fail, because we never reach it. Instead an assert in MemberSemanticModel fails:

Debug.Assert((manager is null && (!Compilation.NullableSemanticAnalysisEnabled || syntax != Root || syntax is TypeSyntax ||

Copy link
Contributor

Choose a reason for hiding this comment

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

In the old iteration, this assert does not fail, because we never reach it. Instead an assert in MemberSemanticModel fails:

Could you please confirm that, if you step over that debug assert in the debugger (usually, I am able to continue after debugger breaks execution due to a debug assert while a test is being debugged), then the test (I assume at least one of these asserts) fails the "normal" x-unit way?


In reply to: 463241933 [](ancestors = 463241933)

Copy link
Contributor

Choose a reason for hiding this comment

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

Alternatively, you can execute release flavor of this test. Whichever you find easier.


In reply to: 463296735 [](ancestors = 463296735,463241933)

Copy link
Member Author

@RikkiGibson RikkiGibson Jul 30, 2020

Choose a reason for hiding this comment

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

Thanks for the suggestion to run in Release mode. With that, I find that the Xunit assert failed within the AnalyzeMemberAccess method via an unexpected AD0001 diagnostic:

[xUnit.net 00:00:01.92]     Microsoft.CodeAnalysis.CSharp.UnitTests.NullablePublicAPITests.CSharp73ProvidesNullableSemanticInfo [FAIL]
  X Microsoft.CodeAnalysis.CSharp.UnitTests.NullablePublicAPITests.CSharp73ProvidesNullableSemanticInfo [863ms]
  Error Message:

Expected:
Actual:
    Diagnostic("AD0001").WithArguments("Microsoft.CodeAnalysis.CSharp.UnitTests.NullablePublicAPITests+CSharp73ProvidesNullableSemanticInfo_Analyzer", "Xunit.Sdk.EqualException", "Assert.Equal() Failure
Expected: Annotated
Actual:   None", "Exception occurred with following context:
Compilation: 638fbd72-f58d-40c4-9ddf-c62ce4a9d737
SyntaxTree:
SyntaxNode: s.ToString [MemberAccessExpressionSyntax]@[90..100) (7,12)-(7,22)

Xunit.Sdk.EqualException: Assert.Equal() Failure
Expected: Annotated
Actual:   None
   at Xunit.Assert.Equal[T](T expected, T actual, IEqualityComparer`1 comparer) in C:\projects\xunit\src\xunit.assert\Asserts\EqualityAsserts.cs:line 41
   at Xunit.Assert.Equal[T](T expected, T actual) in C:\projects\xunit\src\xunit.assert\Asserts\EqualityAsserts.cs:line 25
   at Microsoft.CodeAnalysis.CSharp.UnitTests.NullablePublicAPITests.CSharp73ProvidesNullableSemanticInfo_Analyzer.AnalyzeMemberAccess(SyntaxNodeAnalysisContext context) in C:\Users\rikki\src\roslyn\src\Compilers\CSharp\Test\Symbol\Symbols\Source\NullablePublicAPITests.cs:line 1723
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.<>c__60`1.<ExecuteSyntaxNodeAction>b__60_0(ValueTuple`2 data) in C:\Users\rikki\src\roslyn\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 880
   at Microsoft.CodeAnalysis.Diagnostics.AnalyzerExecutor.ExecuteAndCatchIfThrows_NoLock[TArg](DiagnosticAnalyzer analyzer, Action`1 analyze, TArg argument, Nullable`1 info) in C:\Users\rikki\src\roslyn\src\Compilers\Core\Portable\DiagnosticAnalyzer\AnalyzerExecutor.cs:line 1569

@RikkiGibson
Copy link
Member Author

@AlekseyTs I believe I have addressed all your comments. Did you have any more?

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

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

LGTM (iteration 4)

@RikkiGibson RikkiGibson merged commit 2e8584b into dotnet:master Jul 30, 2020
@ghost ghost added this to the Next milestone Jul 30, 2020
@RikkiGibson RikkiGibson deleted the nullable-single-pass branch July 30, 2020 23:39
333fred added a commit to 333fred/roslyn that referenced this pull request Jul 31, 2020
* upstream/master: (304 commits)
  Tweak diagnostics to account for records (dotnet#46341)
  Diagnose precedence inversion in a warning wave (dotnet#46239)
  Remove PROTOTYPE tag (dotnet#45965)
  Only run a single pass of NullableWalker per-member (dotnet#46402)
  Fix crash, and offer "declare as nullable" for tuple fields (dotnet#46437)
  Simplify contract for RunWithShutdownBlockAsync
  Fix optprof plugin input
  check if EditorAdaptersFactoryService gives us a null buffer
  Cannot assign maybe-null value to TNotNull variable (dotnet#41445)
  Fix overload resolution to handle binary compat in the face of covariant returns (dotnet#46367)
  Same failure on Linux
  Skip some tests on Mac
  Added search option for inline parameter name hints
  Spelling
  tweak docs
  Improve comment
  Improve the "not exhaustive" diagnostic in the presence of a when clause. (dotnet#46143)
  PR feedback
  Use record keyword to display records (dotnet#46338)
  remove test that aserts .NET Standard should be prefered over .NET Framework
  ...
@RikkiGibson RikkiGibson modified the milestones: Next, 16.8.P2 Aug 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Investigate avoiding work if not rewriting for nullable

5 participants