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

Fix crash in 'use object/collection initializer' in VB. #70028

Merged
merged 13 commits into from
Sep 20, 2023

Conversation

CyrusNajmabadi
Copy link
Member

Fixes #70015

@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner September 20, 2023 00:09
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Sep 20, 2023
@@ -89,6 +89,7 @@
<Compile Include="$(MSBuildThisFileDirectory)UseCollectionExpression\CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs" />
Copy link
Member Author

Choose a reason for hiding this comment

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

view PR with whitespace off.

public static bool IsInitializerOfLocalDeclarationStatement(
LocalDeclarationStatementSyntax localDeclarationStatement,
BaseObjectCreationExpressionSyntax rootExpression,
[NotNullWhen(true)] out VariableDeclaratorSyntax? variableDeclarator)
Copy link
Member Author

Choose a reason for hiding this comment

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

crux of the change was that we had some suspect code working at the raw node level, trying to understand c# and vb nodes in a uniform way. That code was simply broken. We now defer to each language to handle this case as VB has some unique constructs we can't handle uniformly.

Copy link
Member Author

Choose a reason for hiding this comment

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

this function is shared between UseCollectionInitializer and UseObjectInitializer, which is why it's a shared helper they can both use.

ExpressionStatementSyntax,
LocalDeclarationStatementSyntax,
VariableDeclaratorSyntax,
CSharpUseNamedMemberInitializerAnalyzer>
Copy link
Member Author

Choose a reason for hiding this comment

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

previously UseNamedMemberInitializerAnalyzer was a concrete type that worked on both C# and VB. but it was handling this case wrong, and each language has specific logic it needs. So this moved to become an abstract type, and there is now a C# and VB specific subclass.

CSharpUseNamedMemberInitializerAnalyzer>
{
protected override bool IsInitializerOfLocalDeclarationStatement(LocalDeclarationStatementSyntax localDeclarationStatement, BaseObjectCreationExpressionSyntax rootExpression, out VariableDeclaratorSyntax variableDeclarator)
=> CSharpObjectCreationHelpers.IsInitializerOfLocalDeclarationStatement(localDeclarationStatement, rootExpression, out variableDeclarator);
Copy link
Member Author

Choose a reason for hiding this comment

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

calls into shared helper.

@@ -24,6 +24,7 @@ internal sealed class CSharpUseCollectionInitializerDiagnosticAnalyzer :
MemberAccessExpressionSyntax,
InvocationExpressionSyntax,
ExpressionStatementSyntax,
LocalDeclarationStatementSyntax,
Copy link
Member Author

Choose a reason for hiding this comment

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

passing this around as a strongly typed type-argument so that the signatures are correct in each language.

TStatementSyntax containingStatement,
CancellationToken cancellationToken)
{
if (containingStatement is not TLocalDeclarationStatementSyntax localDeclarationStatement)
Copy link
Member Author

Choose a reason for hiding this comment

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

this function update is the meat of the change. Previously it made assumptions about the shape that didn't hold in VB. now it calls into each language to deal with the specific shapes, and only uses shared code once it knows it can.

{
var seenInvocation = false;
var seenIndexAssignment = false;
protected sealed override bool TryAddMatches(
Copy link
Member Author

Choose a reason for hiding this comment

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

sealed members in abstract classes to make inheritance easier to reason about.

internal class UseNamedMemberInitializerAnalyzer<
namespace Microsoft.CodeAnalysis.UseObjectInitializer;

internal abstract class AbstractUseNamedMemberInitializerAnalyzer<
Copy link
Member Author

Choose a reason for hiding this comment

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

this pattern matches what we do in AbstractUseCOllectionInitializerAnalyzer. It's verbose in terms of the decl here and the generic constraints, but it makes the typing of all the subclasses nicer.


Imports Microsoft.CodeAnalysis.VisualBasic.Syntax

Namespace Microsoft.CodeAnalysis.VisualBasic.UseCollectionInitializer
Copy link
Member Author

Choose a reason for hiding this comment

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

VB impl of the helper.

Imports Microsoft.CodeAnalysis.VisualBasic.UseCollectionInitializer

Namespace Microsoft.CodeAnalysis.VisualBasic.UseObjectInitializer
Friend NotInheritable Class VisualBasicUseNamedMemberInitializerAnalyzer
Copy link
Member Author

Choose a reason for hiding this comment

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

VB impl of the abstract analyzer type.

@CyrusNajmabadi
Copy link
Member Author

@ryzngard ptal. Thanks!

protected abstract bool IsInitializerOfLocalDeclarationStatement(
TLocalDeclarationStatementSyntax localDeclarationStatement, TObjectCreationExpressionSyntax rootExpression, [NotNullWhen(true)] out TVariableDeclaratorSyntax? variableDeclarator);

private static readonly ObjectPool<TAnalyzer> s_pool = SharedPools.Default<TAnalyzer>();
Copy link
Member Author

Choose a reason for hiding this comment

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

this (and the two methods below) moved up to here from derived type.

TVariableDeclaratorSyntax,
TAnalyzer>, new()
{
private static readonly ObjectPool<TAnalyzer> s_pool = SharedPools.Default<TAnalyzer>();
Copy link
Member Author

Choose a reason for hiding this comment

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

move to base type. works for 'use collection init' and 'use object init'.

@CyrusNajmabadi
Copy link
Member Author

@akhera99 @genlu @ryzngard ptal.

@CyrusNajmabadi CyrusNajmabadi merged commit 9a4b26d into dotnet:main Sep 20, 2023
24 checks passed
@ghost ghost added this to the Next milestone Sep 20, 2023
@CyrusNajmabadi CyrusNajmabadi deleted the vbCrash branch September 20, 2023 20:15
@Cosifne Cosifne modified the milestones: Next, 17.8 P3 Sep 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
3 participants