Skip to content

Fix NRE in VisualBasicAddExplicitCastCodeFixProvider #62075

Merged
dibarbet merged 5 commits intodotnet:mainfrom
kfechter:main
Jun 27, 2022
Merged

Fix NRE in VisualBasicAddExplicitCastCodeFixProvider #62075
dibarbet merged 5 commits intodotnet:mainfrom
kfechter:main

Conversation

@kfechter
Copy link
Copy Markdown
Contributor

Fixes issue #61538

NRE no longer occurs when a class attribute is used that triggers BC30519. Cast suggestions are now provided.

image

@kfechter kfechter requested a review from a team as a code owner June 22, 2022 15:05
@ghost ghost added Area-IDE Community The pull request was submitted by a contributor who is not a Microsoft employee. labels Jun 22, 2022
Comment on lines +77 to +78
Dim invocationNodeExpression = spanNode.GetAncestors(Of ExpressionSyntax).FirstOrDefault(
Function(node) Not node.ChildNodes.OfType(Of ArgumentListSyntax).IsEmpty())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
Dim invocationNodeExpression = spanNode.GetAncestors(Of ExpressionSyntax).FirstOrDefault(
Function(node) Not node.ChildNodes.OfType(Of ArgumentListSyntax).IsEmpty())
Dim invocationNodeExpression = spanNode.GetAncestors(Of InvocationExpressionSyntax).FirstOrDefault(
Function(node) node.ArgumentList is not nothing)

?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Unclear as to what this change is for.

Not against making the change, Just need clarification on why this is better than the code that was originally there.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

instead of checking every arbitrary node, you are only looking at invocations. and because you're only looking at invocatinos, you don't have to enumerate all child nodes looking for argumentlists, you can just directly check if it has an argument list or not.

(the same feedback applies to the check below this for atttributes as well).

thanks!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Gotcha! I'll make that change tonight and test it once I'm off work

thanks.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Code change made and tested locally, just working through adding the unit test.

@genlu
Copy link
Copy Markdown
Member

genlu commented Jun 22, 2022

Hey @kfechter, thanks for your contribution! Could you please also add a unittest here to cover the scenario?

@kfechter
Copy link
Copy Markdown
Contributor Author

Hey @kfechter, thanks for your contribution! Could you please also add a unittest here to cover the scenario?

I'm trying to work through adding this unit test but I'm hitting a bit of a roadblock. I'm positive this is just due to my not understanding how the unit tests work, but I've added one by looking at what the others are doing and trying to replicate that with this scenario, but the test just doesn't seem to be working.

The below is what I am trying to do to implement this test.

        <Fact(), Trait(Traits.Feature, Traits.Features.CodeActionsAddExplicitCast)>
        Public Async Function TestClassAttribute() As Task
            Await TestInRegularAndScriptAsync(
"Option Strict On
Class Program
    <System.Runtime.InteropServices.ClassInterface(|1|)>
    Class Base
    End Class
End Class",
"Option Strict On
Class Program
    <System.Runtime.InteropServices.ClassInterface(CShort(1))>
    Class Base
    End Class
End Class")
        End Function

however, with the above code, I get the following error
System.InvalidOperationException : Sequence contains no elements

This is my first time delving into these unit tests, so I'm positive I'm just missing something super simple

@dibarbet
Copy link
Copy Markdown
Member

dibarbet commented Jun 23, 2022

however, with the above code, I get the following error
System.InvalidOperationException : Sequence contains no elements

This might not be the only cause, but I think the caret location marker is missing brackets. Your code shows

ClassInterface(|1|)

but I believe it should be

ClassInterface([|1|])

e.g. https://github.com/dotnet/roslyn/blob/main/src/Analyzers/VisualBasic/Tests/AddExplicitCast/AddExplicitCastTests.vb#L30

@kfechter
Copy link
Copy Markdown
Contributor Author

however, with the above code, I get the following error
System.InvalidOperationException : Sequence contains no elements

This might not be the only cause, but I think the caret location marker is missing brackets. Your code shows

ClassInterface(|1|)

but I believe it should be

ClassInterface([|1|])

e.g. https://github.com/dotnet/roslyn/blob/main/src/Analyzers/VisualBasic/Tests/AddExplicitCast/AddExplicitCastTests.vb#L30

ah ha. I'm getting further now. It throws "No action was offered when one was expected." but i'm probably missing something in the class definition that the test framework is expecting.

@kfechter
Copy link
Copy Markdown
Contributor Author

I think I have it working. Just need to do a tiny bit more testing.


<Fact(), Trait(Traits.Feature, Traits.Features.CodeActionsAddExplicitCast)>
Public Async Function TestClassAttribute() As Task
Await TestMissingInRegularAndScriptAsync(
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.

So this tests that the code action is missing, but we should be testing that it is present. The test snippet you posted before I think was the correct way to go.

I did notice though that the diagnostic only shows up when Option Strict Off is set. So for your old test code you may be able to fix it just by changing the On to Off

Copy link
Copy Markdown
Contributor Author

@kfechter kfechter Jun 23, 2022

Choose a reason for hiding this comment

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

ah, that explains why I couldn't get the test to fail. I will revert that tomorrow. I left the power adapter for my Dev laptop at work.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Reverted and set Option Strict Off, but now I'm getting

System.Exception : No action was offered when one was expected.

I debugged, and it looks like the CodeAction is null, but i'm not understanding how the test framework actually gets the CodeActions to perform. below is my most recent test code.

        <Fact(), Trait(Traits.Feature, Traits.Features.CodeActionsAddExplicitCast)>
        Public Async Function TestClassAttribute() As Task
            Await TestInRegularAndScriptAsync(
"Option Strict Off
Imports System.Runtime.InteropServices
<ClassInterface([|1|])>
Class Program
End Class",
"Option Strict Off
Imports System.Runtime.InteropServices
<ClassInterface(CShort(1))>
Class Program
End Class")
        End Function

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.

Hmm - nothing immediately obvious stands out to me as being wrong. Potentially the test workspace doesn't have the right reference for System.Runtime.InteropServices? Not sure that is the case though. I can check out this branch tomorrow (its the end of my work day now) and see if I can help debug where its going wrong and report back.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i'd recommend not testing this exact interface, but rather just include a simple copy of it in the test.

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.

@kfechter I finally got it working - looks like I needed to pass the option strict as a compilation option instead. The change I made is here - dibarbet@5cf383c

I can either update your branch here or I can leave it to you to update the test, whichever you prefer.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I can update the test. I appreciate the help!

Dim invocationNodeExpression = spanNode.GetAncestors(Of InvocationExpressionSyntax).FirstOrDefault(
Function(node) node.ArgumentList IsNot Nothing)

Dim invocationNodeAttribute = spanNode.GetAncestors(Of AttributeSyntax).FirstOrDefault(
Copy link
Copy Markdown
Contributor

@CyrusNajmabadi CyrusNajmabadi Jun 23, 2022

Choose a reason for hiding this comment

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

Suggested change
Dim invocationNodeAttribute = spanNode.GetAncestors(Of AttributeSyntax).FirstOrDefault(
Dim attributeNode = spanNode.GetAncestors(Of AttributeSyntax).FirstOrDefault(

Copy link
Copy Markdown
Member

@dibarbet dibarbet left a comment

Choose a reason for hiding this comment

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

lgtm! Our main branch is locked down due to some infra issues, but I'll merge this when it opens. Thanks!!

@dibarbet dibarbet merged commit 9bf8d8d into dotnet:main Jun 27, 2022
@ghost ghost added this to the Next milestone Jun 27, 2022
@RikkiGibson RikkiGibson modified the milestones: Next, 17.3 P3 Jun 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-IDE Community The pull request was submitted by a contributor who is not a Microsoft employee.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants