Skip to content
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

Issue 33798: Prefer Dictionary<K, V>.TryGetValue() over guarded indexer access #4851

Merged
merged 21 commits into from
Jun 1, 2022

Conversation

CollinAlpert
Copy link
Contributor

Added analyzer and code fix for guarded dictionary indexer accesses.
The analyzer looks for instances of a dictionary indexer access and checks if it is guarded by a ContainsKey check. If so, a diagnostic is raised and a code fix is offered. When applied, the ContainsKey check is replaced with a TryGetValue invocation.
Fixes issue #33798.

@CollinAlpert CollinAlpert requested a review from a team as a code owner February 15, 2021 19:22
@dnfadmin
Copy link

dnfadmin commented Feb 15, 2021

CLA assistant check
All CLA requirements met.

# Conflicts:
#	src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
var dictionaryAccessNode = root.FindNode(dictionaryAccessLocation.SourceSpan);
var dictionaryAccess = dictionaryAccessNode as ElementAccessExpressionSyntax ?? (dictionaryAccessNode as ArgumentSyntax)?.Expression as ElementAccessExpressionSyntax;
if (dictionaryAccess is null
|| root.FindNode(context.Span) is not InvocationExpressionSyntax containsKeyInvocation
Copy link
Member

Choose a reason for hiding this comment

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

Same as above, do we need getInnermostNodeForTie here? If so, needs a test that demonstrates that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We do not, as I still need to access the InvocationExpressionSyntax later on.

Copy link
Member

Choose a reason for hiding this comment

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

if we do not need InvocationExpressionSyntax set, should we remove this?

}

var additionalLocations = ImmutableArray.Create(propertyReference.Syntax.GetLocation());
context.ReportDiagnostic(Diagnostic.Create(ContainsKeyRule, containsKeyInvocation.Syntax.GetLocation(), additionalLocations));
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why? What advantages do I gain?

@buyaa-n
Copy link
Member

buyaa-n commented Feb 25, 2021

The analyzer looks for instances of a dictionary indexer access and checks if it is guarded by a ContainsKey check. If so, a diagnostic is raised and a code fix is offered. When applied, the ContainsKey check is replaced with a TryGetValue invocation.

@CollinAlpert I think it might be better to start with ContainsKey conditional operation because the conditional operation should have much less occurrence than property references, another reason for doing that is because of the below comment

Fixes issue #33798.

Thanks, i see dotnet/runtime#33799 is also assigned to you, you must have already noticed that they are very closely related and preferably one analyzer (could have a different ID if needed). What are you planning for that analyzer? i think to cover both it's better lookup ContainsKey conditional operation first and then check if there is indexer or Add method used, what you think? There is actually 3rd similar analyzer which was better to be handled together but seems somebody else claimed for that, anyway these analyzers at least should not fight with each other.

@buyaa-n buyaa-n requested a review from mavasani May 19, 2022 23:10
@buyaa-n
Copy link
Member

buyaa-n commented May 27, 2022

Seems need to run dotnet msbuild -t:pack again build failures caused by generated files

# Conflicts:
#	src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
@CollinAlpert
Copy link
Contributor Author

@buyaa-n I just merged the main branch and am now getting an exception when running dotnet msbuild -t:pack:

Unhandled exception. System.Threading.Tasks.TaskCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.
   ---> System.TimeoutException: A task was canceled.
   ---> System.Threading.Tasks.TaskCanceledException: A task was canceled.
     at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
     at System.Net.Http.HttpConnectionPool.GetHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
     at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
     at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
     at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
     --- End of inner exception stack trace ---
     --- End of inner exception stack trace ---
     at System.Net.Http.HttpClient.HandleFailure(Exception e, Boolean telemetryStarted, HttpResponseMessage response, CancellationTokenSource cts, CancellationToken cancellationToken, CancellationTokenSource pendingRequestsCts)
     at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
     at GenerateDocumentationAndConfigFiles.Program.<>c__DisplayClass1_0.<<Main>g__checkHelpLinkAsync|16>d.MoveNext() in /Users/collin/Programmieren/dotNET/roslyn-analyzers/src/Tools/GenerateDocumentationAndConfigFiles/Program.cs:line 650
  --- End of stack trace from previous location ---
     at GenerateDocumentationAndConfigFiles.Program.<>c__DisplayClass1_0.<<Main>g__createAnalyzerRulesMissingDocumentationFileAsync|10>d.MoveNext() in /Users/collin/Programmieren/dotNET/roslyn-analyzers/src/Tools/GenerateDocumentationAndConfigFiles/Program.cs:line 593
  --- End of stack trace from previous location ---
     at GenerateDocumentationAndConfigFiles.Program.Main(String[] args) in /Users/collin/Programmieren/dotNET/roslyn-analyzers/src/Tools/GenerateDocumentationAndConfigFiles/Program.cs:line 199
     at GenerateDocumentationAndConfigFiles.Program.<Main>(String[] args)

@buyaa-n
Copy link
Member

buyaa-n commented May 31, 2022

I just merged the main branch and am now getting an exception when running dotnet msbuild -t:pack

Probably caused by that missing quota, if that not fixes the issue try reverting all changes in all generated files:

src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md
src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif
src/NetAnalyzers/RulesMissingDocumentation.md

And run the dotnet msbuild -t:pack

Copy link
Member

@buyaa-n buyaa-n left a comment

Choose a reason for hiding this comment

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

Looks good to me, thank you for your contribution @CollinAlpert

@buyaa-n buyaa-n merged commit a11d5d3 into dotnet:main Jun 1, 2022
@github-actions github-actions bot added this to the vNext milestone Jun 1, 2022
@CollinAlpert CollinAlpert deleted the issue-33798 branch June 3, 2022 13:12
@jmarolf jmarolf modified the milestones: vNext, .NET 7.x Nov 15, 2022
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.

Prefer Dictionary<K, V>.TryGetValue() over guarded indexer access
8 participants