Skip to content

Nullable enable analyzer driver#46409

Merged
mavasani merged 7 commits intodotnet:masterfrom
mavasani:AnalyzerDriverNullableEnable
Aug 12, 2020
Merged

Nullable enable analyzer driver#46409
mavasani merged 7 commits intodotnet:masterfrom
mavasani:AnalyzerDriverNullableEnable

Conversation

@mavasani
Copy link
Copy Markdown
Contributor

No description provided.

@mavasani mavasani force-pushed the AnalyzerDriverNullableEnable branch 3 times, most recently from 5419a66 to fd826a5 Compare July 29, 2020 18:39
@mavasani mavasani force-pushed the AnalyzerDriverNullableEnable branch from fd826a5 to b819e2f Compare July 29, 2020 22:01
@mavasani mavasani marked this pull request as ready for review July 29, 2020 23:42
@mavasani mavasani requested a review from a team as a code owner July 29, 2020 23:42
@mavasani mavasani requested review from jaredpar and jcouv July 29, 2020 23:43
Comment thread src/Compilers/Core/AnalyzerDriver/DeclarationInfo.cs
Copy link
Copy Markdown
Contributor

@sharwell sharwell left a comment

Choose a reason for hiding this comment

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

I've completed two files so far. It's going to take me a long time to get through the rest of this because there are so many unrelated changes.

Comment thread src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs
Comment thread src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs
Comment thread src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs
Comment thread src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs
Comment thread src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs
Comment thread src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs
@mavasani
Copy link
Copy Markdown
Contributor Author

mavasani commented Aug 7, 2020

I've completed two files so far. It's going to take me a long time to get through the rest of this because there are so many unrelated changes.

Can you please clarify? Removing Opt suffix absolutely falls within the scope of this change as those suffixes were present due to files not being nullable enabled. Changing x = x ?? new X() to x ??= new X() are extremely trivial changes also related to null-ness and do not harm the review ability for the PR. Forcing these into separate PRs leads to multiple review cycles for similar cleanup changes, and lot of times also get dropped. I would prefer to keep all these changes as a single "null-ness enable/cleanup" PR.

Comment thread src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticQueue.cs Outdated
Comment thread src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticQueue.cs Outdated
Comment thread src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs
Comment thread src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs
Comment thread src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs
Comment thread src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs Outdated
@jcouv
Copy link
Copy Markdown
Member

jcouv commented Aug 10, 2020

        Debug.Assert(_lazyGeneratedCodeAttribute != null);

How do we know this?
The initialization _lazyGeneratedCodeAttribute = analyzerExecutor.Compilation?.GetTypeByMetadataName("System.CodeDom.Compiler.GeneratedCodeAttribute"); could leave this variable as null #Closed


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:1153 in b819e2f. [](commit_id = b819e2f, deletion_comment = False)

@jcouv
Copy link
Copy Markdown
Member

jcouv commented Aug 10, 2020

            if (_lazyInitializeTask == null)

nit: not related to this PR, but I didn't follow how _lazyInitializeTask could still be null here #Closed


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:417 in b819e2f. [](commit_id = b819e2f, deletion_comment = False)

@jcouv
Copy link
Copy Markdown
Member

jcouv commented Aug 10, 2020

        Debug.Assert(WhenInitializedTask != null);

Consider keeping the assertion Never mind, it's in the property now :-) #Closed


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:520 in b819e2f. [](commit_id = b819e2f, deletion_comment = True)

@jcouv
Copy link
Copy Markdown
Member

jcouv commented Aug 10, 2020

        analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue!, analysisScope, cancellationToken: cancellationToken);

This probably should be an assertion (I don't see a related check in this method) #Closed


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:854 in b819e2f. [](commit_id = b819e2f, deletion_comment = False)

@333fred
Copy link
Copy Markdown
Member

333fred commented Aug 11, 2020

            if (tree != null &&

Consider inlining this with a pattern, so the suppression would not be needed. #Closed


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:1926 in 21a46d1. [](commit_id = 21a46d1, deletion_comment = False)

@mavasani
Copy link
Copy Markdown
Contributor Author

Debug.Assert(_lazyGeneratedCodeAttribute != null);

How do we know this?
The initialization _lazyGeneratedCodeAttribute = analyzerExecutor.Compilation?.GetTypeByMetadataName("System.CodeDom.Compiler.GeneratedCodeAttribute"); could leave this variable as null

The only place where the method was being called had a null check for _lazyGeneratedCodeAttribute. I have made this method now be a local function for the method with that callsite, so this is much more explicit.

@mavasani
Copy link
Copy Markdown
Contributor Author

Debug.Assert(location.SourceTree != null);

Consider leaving a comment on Location.IsInSource. We should annotate it with MemberNotNullWhen. Referencing #41964

Ah, I filed #46729. Feel free to resolve that as a dupe of #41964 if that is preferable.

@333fred
Copy link
Copy Markdown
Member

333fred commented Aug 11, 2020

    internal (Compilation? compilation, AnalyzerOptions? analyzerOptions) TryGetCompilationAndAnalyzerOptions() => (_compilation, _analyzerOptions);

Should we prefer a bool returning method with outs, and NotNullWhens on the outs here? #Closed


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs:228 in 21a46d1. [](commit_id = 21a46d1, deletion_comment = False)

@333fred
Copy link
Copy Markdown
Member

333fred commented Aug 11, 2020

    internal ImmutableDictionary<DiagnosticAnalyzer, TimeSpan> AnalyzerExecutionTimes => _analyzerExecutionTimeMap!.ToImmutableDictionary(pair => pair.Key, pair => TimeSpan.FromTicks(pair.Value.Value));

Why is this safe? I would prefer an assert. #Closed


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs:250 in 21a46d1. [](commit_id = 21a46d1, deletion_comment = False)

@mavasani
Copy link
Copy Markdown
Contributor Author

analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue!, analysisScope, cancellationToken: cancellationToken);

This probably should be an assertion (I don't see a related check in this method)

We create newCompilation explicitly with an event queue just a couple of lines above:

newCompilation = compilation.WithEventQueue(new AsyncQueue<CompilationEvent>());
var categorizeDiagnostics = false;
var analysisOptions = new CompilationWithAnalyzersOptions(options, onAnalyzerException, analyzerExceptionFilter: analyzerExceptionFilter, concurrentAnalysis: true, logAnalyzerExecutionTime: reportAnalyzer, reportSuppressedDiagnostics: false);
analyzerDriver.Initialize(newCompilation, analysisOptions, new CompilationData(newCompilation), categorizeDiagnostics, cancellationToken);
var analysisScope = new AnalysisScope(newCompilation, options, analyzers, hasAllAnalyzers: true, concurrentAnalysis: newCompilation.Options.ConcurrentBuild, categorizeDiagnostics: categorizeDiagnostics);
analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue, analysisScope, cancellationToken: cancellationToken);

@mavasani
Copy link
Copy Markdown
Contributor Author

Debug.Assert(tree != null);

We could keep the assertion

Same as previous comment - this is in a private method with all callers in this file.

@333fred
Copy link
Copy Markdown
Member

333fred commented Aug 11, 2020

Done review pass (commit 2). In future, I'd prefer if nullable enable PRs were a bit smaller: when I review a file that has been #nullable enabled for the first time, it involves reviewing the entire file. There were multiple 2k+ line files in this PR, which makes for a very slow review. #Closed

@mavasani
Copy link
Copy Markdown
Contributor Author

 onAnalyzerException ??= (ex, analyzer, diagnostic) => { };

Did we have a bug? If yes, can we cover with a test?

Thanks, let me add a test.

// Verify IsDiagnosticAnalyzerSuppressed does not throw an exception when 'onAnalyzerException' is null.
var analyzer = new AnalyzerThatThrowsInSupportedDiagnostics();
var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
_ = CompilationWithAnalyzers.IsDiagnosticAnalyzerSuppressed(analyzer, options, onAnalyzerException: null);
Copy link
Copy Markdown
Contributor Author

@mavasani mavasani Aug 11, 2020

Choose a reason for hiding this comment

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

@jcouv I added this unit test for null onAnalyzerException value passed down to the analyzer executor, but I could not get the test to fail even on master. NRE seems to be silently swallowed below:

try
{
_onAnalyzerException(e, analyzer, diagnostic);
}
catch (Exception)
{
// Ignore exceptions from exception handlers.
}

At least now we ensure no NRE is thrown in the above code by the analyzer executor for this test. #Closed

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Makes sense. Thanks!


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

@mavasani
Copy link
Copy Markdown
Contributor Author

Thanks everyone for the reviews! I think I have responded to and/or addressed all feedback, please let me know if I missed anything.

@333fred
Copy link
Copy Markdown
Member

333fred commented Aug 12, 2020

            if (tree != null &&

I was actually thinking diagnostic is { Location: { SourceTree: { } tree } }, but this works too.


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


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:1926 in 21a46d1. [](commit_id = 21a46d1, deletion_comment = False)

@333fred
Copy link
Copy Markdown
Member

333fred commented Aug 12, 2020

            if (_lazyInitializeTask == null)

That doesn't really matter for flow analysis though. We assume exceptions can occur at any time, for any reason. The cancellation token could have already been cancelled, for example, or the source for the cancellation token have been disposed.


In reply to: 672244161 [](ancestors = 672244161,672240283,671516788)


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:417 in b819e2f. [](commit_id = b819e2f, deletion_comment = False)

Copy link
Copy Markdown
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 6)

@jcouv
Copy link
Copy Markdown
Member

jcouv commented Aug 12, 2020

            if (_lazyInitializeTask == null)

My question is not about flow analysis, but whether we can actually get here with a null value during execution. I'm not seeing how it's possible. Task.Run won't return null if the token was cancelled, from what I see in the docs.

Maybe the answer is "we're want to be defensive to future code changes" (ie. just to be safe), and that's fine.
But assumptions made by flow analysis don't help me see whether it's actually possible or not.


In reply to: 673013938 [](ancestors = 673013938,672244161,672240283,671516788)


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:417 in b819e2f. [](commit_id = b819e2f, deletion_comment = False)

@333fred
Copy link
Copy Markdown
Member

333fred commented Aug 12, 2020

            if (_lazyInitializeTask == null)

If the token is cancelled, it throws. See https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run?view=netcore-3.1#System_Threading_Tasks_Task_Run_System_Action_System_Threading_CancellationToken_.


In reply to: 673016668 [](ancestors = 673016668,673013938,672244161,672240283,671516788)


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:417 in b819e2f. [](commit_id = b819e2f, deletion_comment = False)

…be null at analyzer driver dispose for early cancellation.
@jcouv
Copy link
Copy Markdown
Member

jcouv commented Aug 12, 2020

            if (_lazyInitializeTask == null)

Thanks for clarifying. That's what I was curious about.


In reply to: 673059936 [](ancestors = 673059936,673016668,673013938,672244161,672240283,671516788)


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:417 in b819e2f. [](commit_id = b819e2f, deletion_comment = False)

@jcouv
Copy link
Copy Markdown
Member

jcouv commented Aug 12, 2020

        analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue!, analysisScope, cancellationToken: cancellationToken);

@mavasani I didn't see a resolution


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


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:854 in b819e2f. [](commit_id = b819e2f, deletion_comment = False)

@jcouv
Copy link
Copy Markdown
Member

jcouv commented Aug 12, 2020

        Debug.Assert(tree != null);

What was the resolution?


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


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:2101 in b819e2f. [](commit_id = b819e2f, deletion_comment = True)

@jcouv
Copy link
Copy Markdown
Member

jcouv commented Aug 12, 2020

        analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue!, analysisScope, cancellationToken: cancellationToken);

newCompilation = compilation.WithEventQueue(new AsyncQueue<CompilationEvent>());


In reply to: 673108299 [](ancestors = 673108299,671530244)


Refers to: src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs:854 in b819e2f. [](commit_id = b819e2f, deletion_comment = False)

Copy link
Copy Markdown
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

LGTM Thanks (iteration 7)

@mavasani mavasani merged commit 4320650 into dotnet:master Aug 12, 2020
@ghost ghost added this to the Next milestone Aug 12, 2020
@mavasani mavasani deleted the AnalyzerDriverNullableEnable branch August 12, 2020 21:40
@allisonchou allisonchou modified the milestones: Next, 16.8.P3 Aug 31, 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.

5 participants