diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SymbolCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SymbolCompletionProviderTests.vb index f86adb86077da79d74472ce5ff2c32812556e1cb..378c03268ef5ad556e4eb9694762fcf599b8ef07 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SymbolCompletionProviderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/SymbolCompletionProviderTests.vb @@ -5319,7 +5319,64 @@ End Module VerifyItemExists(text, "ToString") End Sub - + + + Public Sub UnwrapNullableForConditionalFromStructure() + Dim text = +.Value + + VerifyItemExists(text, "b") + VerifyItemIsAbsent(text, "c") + End Sub + + + + Public Sub WithinChainOfConditionalAccess() + Dim text = +.Value + + VerifyItemExists(text, "b") + VerifyItemIsAbsent(text, "c") + End Sub + + + Public Sub DontThrowForNullPropagatingOperatorOnTypeParameter() Dim text = diff --git a/src/Workspaces/VisualBasic/Portable/Extensions/MemberAccessExpressionSyntaxExtensions.vb b/src/Workspaces/VisualBasic/Portable/Extensions/MemberAccessExpressionSyntaxExtensions.vb index 0459b929f08f71b47ef1e90fcd7c0b560cb886f6..a247e121a36eae68729b7460b601b5fd5f9e3d32 100644 --- a/src/Workspaces/VisualBasic/Portable/Extensions/MemberAccessExpressionSyntaxExtensions.vb +++ b/src/Workspaces/VisualBasic/Portable/Extensions/MemberAccessExpressionSyntaxExtensions.vb @@ -68,15 +68,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions End Function - - Public Function GetExpressionOfMemberAccessExpression(memberAccessExpression As MemberAccessExpressionSyntax) As ExpressionSyntax - If memberAccessExpression Is Nothing Then - Return Nothing - End If + + Public Function GetExpressionOfMemberAccessExpression(memberAccessExpression As MemberAccessExpressionSyntax) As ExpressionSyntax + If memberAccessExpression Is Nothing Then + Return Nothing + End If - If memberAccessExpression.Expression IsNot Nothing Then - Return memberAccessExpression.Expression - End If + If memberAccessExpression.Expression IsNot Nothing Then + Return memberAccessExpression.Expression + End If + + ' Maybe we're part of a ConditionalAccessExpression + Dim conditional = memberAccessExpression.GetCorrespondingConditionalAccessExpression() + If conditional IsNot Nothing Then + If conditional.Parent.IsKind(SyntaxKind.ExpressionStatement) AndAlso conditional.Parent.Parent.IsKind(SyntaxKind.WithBlock) AndAlso + conditional.Expression Is Nothing Then + + Return DirectCast(conditional.Parent.Parent, WithBlockSyntax).WithStatement.Expression + End If + + Return conditional.Expression + End If ' we have a member access expression with a null expression, this may be one of the ' following forms: @@ -84,33 +96,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions ' 1) new With { .a = 1, .b = .a <-- .a refers to the anonymous type ' 2) With obj : .m <-- .m refers to the obj type ' 3) new T() With { .a = 1, .b = .a <-- 'a refers to the T type - ' 4) With obj : ?.m <-- .m refers to the obj type Dim current As SyntaxNode = memberAccessExpression - While current IsNot Nothing - If TypeOf current Is AnonymousObjectCreationExpressionSyntax Then - Return DirectCast(current, ExpressionSyntax) - ElseIf TypeOf current Is WithBlockSyntax Then - Dim withBlock = DirectCast(current, WithBlockSyntax) - If memberAccessExpression IsNot withBlock.WithStatement.Expression Then - Return withBlock.WithStatement.Expression - End If - ElseIf TypeOf current Is ObjectMemberInitializerSyntax AndAlso - TypeOf current.Parent Is ObjectCreationExpressionSyntax Then - Return DirectCast(current.Parent, ExpressionSyntax) - ElseIf current.IsKind(SyntaxKind.ConditionalAccessExpression) Then - Dim conditionalAccess = DirectCast(current, ConditionalAccessExpressionSyntax) - - If conditionalAccess.Expression IsNot Nothing Then - Return conditionalAccess.Expression - End If - End If - - current = current.Parent - End While - - Return Nothing - End Function - End Module + While current IsNot Nothing + If TypeOf current Is AnonymousObjectCreationExpressionSyntax Then + Return DirectCast(current, ExpressionSyntax) + ElseIf TypeOf current Is WithBlockSyntax Then + Dim withBlock = DirectCast(current, WithBlockSyntax) + If memberAccessExpression IsNot withBlock.WithStatement.Expression Then + Return withBlock.WithStatement.Expression + End If + ElseIf TypeOf current Is ObjectMemberInitializerSyntax AndAlso + TypeOf current.Parent Is ObjectCreationExpressionSyntax Then + Return DirectCast(current.Parent, ExpressionSyntax) + End If + + current = current.Parent + End While + + Return Nothing + End Function + End Module End Namespace diff --git a/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxNodeExtensions.vb b/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxNodeExtensions.vb index 9b770242bcd9b106253a16b83d4894f1f864aad7..0f87a13db4bbb0c396c2f57e31814f8a18fd9372 100644 --- a/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxNodeExtensions.vb +++ b/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxNodeExtensions.vb @@ -1085,23 +1085,77 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions Return SpecializedCollections.EmptyEnumerable(Of SyntaxNode)() End Function - - Public Iterator Function GetAliasImportsClauses(root As CompilationUnitSyntax) As IEnumerable(Of SimpleImportsClauseSyntax) - For i = 0 To root.Imports.Count - 1 - Dim statement = root.Imports(i) - - For j = 0 To statement.ImportsClauses.Count - 1 - Dim importsClause = statement.ImportsClauses(j) - - If importsClause.Kind = SyntaxKind.SimpleImportsClause Then - Dim simpleImportsClause = DirectCast(importsClause, SimpleImportsClauseSyntax) - - If simpleImportsClause.Alias IsNot Nothing Then - Yield simpleImportsClause - End If - End If - Next - Next - End Function - End Module + + Public Iterator Function GetAliasImportsClauses(root As CompilationUnitSyntax) As IEnumerable(Of SimpleImportsClauseSyntax) + For i = 0 To root.Imports.Count - 1 + Dim statement = root.Imports(i) + + For j = 0 To statement.ImportsClauses.Count - 1 + Dim importsClause = statement.ImportsClauses(j) + + If importsClause.Kind = SyntaxKind.SimpleImportsClause Then + Dim simpleImportsClause = DirectCast(importsClause, SimpleImportsClauseSyntax) + + If simpleImportsClause.Alias IsNot Nothing Then + Yield simpleImportsClause + End If + End If + Next + Next + End Function + + ''' + ''' Given an expression within a tree of s, + ''' finds the that it is part of. + ''' + ''' + ''' + + Friend Function GetCorrespondingConditionalAccessExpression(node As ExpressionSyntax) As ConditionalAccessExpressionSyntax + Dim access As SyntaxNode = node + Dim parent As SyntaxNode = access.Parent + + While parent IsNot Nothing + Select Case parent.Kind + Case SyntaxKind.DictionaryAccessExpression, + SyntaxKind.SimpleMemberAccessExpression + + If DirectCast(parent, MemberAccessExpressionSyntax).Expression IsNot access Then + Return Nothing + End If + + Case SyntaxKind.XmlElementAccessExpression, + SyntaxKind.XmlDescendantAccessExpression, + SyntaxKind.XmlAttributeAccessExpression + + If DirectCast(parent, XmlMemberAccessExpressionSyntax).Base IsNot access Then + Return Nothing + End If + + Case SyntaxKind.InvocationExpression + + If DirectCast(parent, InvocationExpressionSyntax).Expression IsNot access Then + Return Nothing + End If + + Case SyntaxKind.ConditionalAccessExpression + + Dim conditional = DirectCast(parent, ConditionalAccessExpressionSyntax) + If conditional.WhenNotNull Is access Then + Return conditional + ElseIf conditional.Expression IsNot access Then + Return Nothing + End If + + Case Else + Return Nothing + End Select + + access = parent + parent = access.Parent + End While + + Return Nothing + End Function + End Module End Namespace