|
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports System.Diagnostics.CodeAnalysis
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Recommendations
Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Recommendations
Partial Friend Class VisualBasicRecommendationService
Private NotInheritable Class VisualBasicRecommendationServiceRunner
Inherits AbstractRecommendationServiceRunner
Public Sub New(context As VisualBasicSyntaxContext, filterOutOfScopeLocals As Boolean, cancellationToken As CancellationToken)
MyBase.New(context, filterOutOfScopeLocals, cancellationToken)
End Sub
Protected Overrides Function GetLambdaParameterCount(lambdaSyntax As LambdaExpressionSyntax) As Integer
Return lambdaSyntax.SubOrFunctionHeader.ParameterList.Parameters.Count
End Function
Public Overrides Function GetRecommendedSymbols() As RecommendedSymbols
Return New RecommendedSymbols(GetSymbols())
End Function
Private Overloads Function GetSymbols() As ImmutableArray(Of ISymbol)
If _context.SyntaxTree.IsInNonUserCode(_context.Position, _cancellationToken) OrElse
_context.SyntaxTree.IsInSkippedText(_context.Position, _cancellationToken) Then
Return ImmutableArray(Of ISymbol).Empty
End If
Dim node = _context.TargetToken.Parent
If _context.IsGlobalStatementContext Then
Return GetSymbolsForGlobalStatementContext()
ElseIf _context.IsRightOfNameSeparator Then
If node.Kind = SyntaxKind.SimpleMemberAccessExpression Then
Return GetSymbolsForMemberAccessExpression(DirectCast(node, MemberAccessExpressionSyntax))
ElseIf node.Kind = SyntaxKind.QualifiedName Then
Return GetSymbolsForQualifiedNameSyntax(DirectCast(node, QualifiedNameSyntax))
End If
ElseIf _context.SyntaxTree.IsQueryIntoClauseContext(_context.Position, _context.TargetToken, _cancellationToken) Then
Return GetUnqualifiedSymbolsForQueryIntoContext()
ElseIf _context.IsAnyExpressionContext OrElse
_context.IsStatementContext OrElse
_context.IsNameOfContext Then
Return GetUnqualifiedSymbolsForExpressionOrStatementContext()
ElseIf _context.IsTypeContext OrElse _context.IsNamespaceContext Then
Return GetUnqualifiedSymbolsForType()
ElseIf _context.SyntaxTree.IsLabelContext(_context.Position, _context.TargetToken, _cancellationToken) Then
Return GetUnqualifiedSymbolsForLabelContext()
ElseIf _context.SyntaxTree.IsRaiseEventContext(_context.Position, _context.TargetToken, _cancellationToken) Then
Return GetUnqualifiedSymbolsForRaiseEvent()
ElseIf _context.TargetToken.IsKind(SyntaxKind.ForKeyword) Then
Dim symbols = GetUnqualifiedSymbolsForExpressionOrStatementContext().WhereAsArray(AddressOf IsWritableFieldOrLocal)
Return symbols
ElseIf _context.IsNamespaceDeclarationNameContext Then
Return GetSymbolsForNamespaceDeclarationNameContext(Of NamespaceBlockSyntax)()
ElseIf _context.IsEnumBaseListContext Then
Return GetSymbolsForEnumBaseList(container:=Nothing)
End If
Return ImmutableArray(Of ISymbol).Empty
End Function
Public Overrides Function TryGetExplicitTypeOfLambdaParameter(lambdaSyntax As SyntaxNode, ordinalInLambda As Integer, <NotNullWhen(True)> ByRef explicitLambdaParameterType As ITypeSymbol) As Boolean
Dim lambdaExpressionSyntax = DirectCast(lambdaSyntax, LambdaExpressionSyntax)
Dim parameters = lambdaExpressionSyntax.SubOrFunctionHeader.ParameterList.Parameters
If parameters.Count > ordinalInLambda Then
Dim parameterSyntax = parameters(ordinalInLambda)
If parameterSyntax.AsClause IsNot Nothing Then
explicitLambdaParameterType = _context.SemanticModel.GetTypeInfo(parameterSyntax.AsClause.Type, _cancellationToken).Type
Return explicitLambdaParameterType IsNot Nothing
End If
End If
Return False
End Function
Private Function IsWritableFieldOrLocal(symbol As ISymbol) As Boolean
If symbol.Kind() = SymbolKind.Field Then
Dim field = DirectCast(symbol, IFieldSymbol)
Return Not field.IsReadOnly AndAlso Not field.IsConst
End If
If symbol.Kind() = SymbolKind.Local Then
Dim local = DirectCast(symbol, ILocalSymbol)
Return Not local.IsConst
End If
Return False
End Function
Private Function GetSymbolsForGlobalStatementContext() As ImmutableArray(Of ISymbol)
Return _context.SemanticModel.LookupSymbols(_context.TargetToken.Span.End)
End Function
Private Function GetUnqualifiedSymbolsForQueryIntoContext() As ImmutableArray(Of ISymbol)
Dim symbols = _context.SemanticModel _
.LookupSymbols(_context.TargetToken.SpanStart, includeReducedExtensionMethods:=True)
Return ImmutableArray(Of ISymbol).CastUp(
symbols.OfType(Of IMethodSymbol)().
Where(Function(m) m.IsAggregateFunction()).
ToImmutableArray())
End Function
Private Function GetUnqualifiedSymbolsForLabelContext() As ImmutableArray(Of ISymbol)
Return _context.SemanticModel.LookupLabels(_context.TargetToken.SpanStart)
End Function
Private Function GetUnqualifiedSymbolsForRaiseEvent() As ImmutableArray(Of ISymbol)
Dim containingType = _context.SemanticModel.GetEnclosingSymbol(_context.Position, _cancellationToken).ContainingType
Return _context.SemanticModel _
.LookupSymbols(_context.Position, container:=containingType) _
.WhereAsArray(Function(s) s.Kind = SymbolKind.Event AndAlso Equals(s.ContainingType, containingType))
End Function
Private Function GetUnqualifiedSymbolsForType() As ImmutableArray(Of ISymbol)
Dim symbols = _context.SemanticModel.LookupNamespacesAndTypes(_context.TargetToken.SpanStart)
Return FilterToValidAccessibleSymbols(symbols)
End Function
Private Function GetUnqualifiedSymbolsForExpressionOrStatementContext() As ImmutableArray(Of ISymbol)
Dim lookupPosition = _context.TargetToken.SpanStart
If _context.FollowsEndOfStatement Then
lookupPosition = _context.Position
End If
Dim symbols = If(
Not _context.IsNameOfContext AndAlso _context.TargetToken.Parent.IsInStaticContext(),
_context.SemanticModel.LookupStaticMembers(lookupPosition),
_context.SemanticModel.LookupSymbols(lookupPosition))
If _filterOutOfScopeLocals Then
symbols = symbols.WhereAsArray(Function(symbol) Not symbol.IsInaccessibleLocal(_context.Position))
End If
' GitHub #4428: When the user is typing a predicate (eg. "Enumerable.Range(0,10).Select($$")
' "Func(Of" tends to get in the way of typing "Function". Exclude System.Func from expression
' contexts, except within GetType
If Not _context.TargetToken.IsKind(SyntaxKind.OpenParenToken) OrElse
Not _context.TargetToken.Parent.IsKind(SyntaxKind.GetTypeExpression) Then
symbols = symbols.WhereAsArray(Function(s) Not IsInEligibleDelegate(s))
End If
' Hide backing fields and events
Return symbols.WhereAsArray(Function(s) FilterEventsAndGeneratedSymbols(Nothing, s))
End Function
Private Shared Function IsInEligibleDelegate(s As ISymbol) As Boolean
If s.IsDelegateType() Then
Dim typeSymbol = DirectCast(s, ITypeSymbol)
Return typeSymbol.SpecialType <> SpecialType.System_Delegate
End If
Return False
End Function
Private Function GetSymbolsForQualifiedNameSyntax(node As QualifiedNameSyntax) As ImmutableArray(Of ISymbol)
' We're in a name-only context, since if we were an expression we'd be a
' MemberAccessExpressionSyntax. Thus, let's do other namespaces and types.
Dim leftHandSymbolInfo = _context.SemanticModel.GetSymbolInfo(node.Left, _cancellationToken)
Dim leftHandSymbol = TryCast(leftHandSymbolInfo.Symbol, INamespaceOrTypeSymbol)
Dim couldBeMergedNamespace = ContainsNamespaceCandidateSymbols(leftHandSymbolInfo)
If leftHandSymbol Is Nothing AndAlso Not couldBeMergedNamespace Then
Return ImmutableArray(Of ISymbol).Empty
End If
If TypeOf node.GetAncestor(Of AsClauseSyntax)()?.Parent Is EnumStatementSyntax Then
Return GetSymbolsForEnumBaseList(leftHandSymbol)
End If
Dim symbols As ImmutableArray(Of ISymbol)
If couldBeMergedNamespace Then
symbols = leftHandSymbolInfo.CandidateSymbols.OfType(Of INamespaceSymbol)() _
.SelectMany(Function(n) _context.SemanticModel.LookupNamespacesAndTypes(node.SpanStart, n)) _
.ToImmutableArray()
Else
symbols = _context.SemanticModel _
.LookupNamespacesAndTypes(position:=node.SpanStart, container:=leftHandSymbol)
If _context.IsNamespaceDeclarationNameContext Then
Dim declarationSyntax = node.GetAncestor(Of NamespaceBlockSyntax)
symbols = symbols.WhereAsArray(Function(symbol) IsNonIntersectingNamespace(symbol, declarationSyntax))
End If
End If
Return FilterToValidAccessibleSymbols(symbols)
End Function
Private Function GetSymbolsForMemberAccessExpression(node As MemberAccessExpressionSyntax) As ImmutableArray(Of ISymbol)
Dim leftExpression = node.GetExpressionOfMemberAccessExpression(allowImplicitTarget:=True)
If leftExpression Is Nothing Then
Return ImmutableArray(Of ISymbol).Empty
End If
Dim leftHandTypeInfo = _context.SemanticModel.GetTypeInfo(leftExpression, _cancellationToken)
Dim leftHandSymbolInfo = _context.SemanticModel.GetSymbolInfo(leftExpression, _cancellationToken)
' https://github.com/dotnet/roslyn/issues/9087: Try to speculatively bind a type as an expression for My namespace
' We'll get a type contained in the My Namespace if this is successful
If leftHandTypeInfo.Type IsNot Nothing AndAlso leftHandTypeInfo.Type.Equals(leftHandSymbolInfo.Symbol) Then
Dim leftHandSpeculativeBinding = _context.SemanticModel.GetSpeculativeSymbolInfo(_context.Position, leftExpression, SpeculativeBindingOption.BindAsExpression)
If leftHandSpeculativeBinding.Symbol IsNot Nothing AndAlso
leftHandSpeculativeBinding.Symbol.ContainingNamespace?.IsMyNamespace(_context.SemanticModel.Compilation) Then
leftHandSymbolInfo = leftHandSpeculativeBinding
End If
End If
Dim excludeInstance = False
Dim excludeShared = True ' do not show shared members by default
Dim useBaseReferenceAccessibility = False
Dim inNameOfExpression = node.IsParentKind(SyntaxKind.NameOfExpression)
Dim container As ISymbol = leftHandTypeInfo.Type
If container Is Nothing AndAlso TypeOf (leftHandTypeInfo.ConvertedType) Is IArrayTypeSymbol Then
container = leftHandTypeInfo.ConvertedType
End If
If container.IsErrorType() AndAlso leftHandSymbolInfo.Symbol IsNot Nothing Then
' TODO remove this when 531549 which causes leftHandTypeInfo to be an error type is fixed
container = leftHandSymbolInfo.Symbol.GetSymbolType()
End If
Dim couldBeMergedNamespace = False
If leftHandSymbolInfo.Symbol IsNot Nothing Then
Dim firstSymbol = leftHandSymbolInfo.Symbol
If firstSymbol.Kind = SymbolKind.Alias Then
firstSymbol = DirectCast(firstSymbol, IAliasSymbol).Target
End If
Select Case firstSymbol.Kind
Case SymbolKind.TypeParameter
' 884060: We don't allow invocations off type parameters.
Return ImmutableArray(Of ISymbol).Empty
Case SymbolKind.NamedType, SymbolKind.Namespace
excludeInstance = True
excludeShared = False
container = DirectCast(firstSymbol, INamespaceOrTypeSymbol)
End Select
If firstSymbol.Kind = SymbolKind.Parameter Then
Dim parameter = DirectCast(firstSymbol, IParameterSymbol)
If parameter.IsMe Then
excludeShared = False
' case:
' MyBase.
useBaseReferenceAccessibility = Not parameter.Type.Equals(container)
End If
container = parameter
End If
' Check for color color
Dim speculativeTypeBinding = _context.SemanticModel.GetSpeculativeTypeInfo(_context.Position, leftExpression, SpeculativeBindingOption.BindAsTypeOrNamespace)
Dim speculativeAliasBinding = _context.SemanticModel.GetSpeculativeAliasInfo(_context.Position, leftExpression, SpeculativeBindingOption.BindAsTypeOrNamespace)
If TypeOf leftHandSymbolInfo.Symbol IsNot INamespaceOrTypeSymbol AndAlso speculativeAliasBinding Is Nothing AndAlso Equals(firstSymbol.GetSymbolType(), speculativeTypeBinding.Type) Then
excludeShared = False
excludeInstance = False
End If
If inNameOfExpression Then
excludeInstance = False
End If
If container Is Nothing OrElse TryCast(container, ITypeSymbol)?.TypeKind = TypeKind.Enum Then
excludeShared = False ' need to allow shared members for enums
End If
Else
couldBeMergedNamespace = ContainsNamespaceCandidateSymbols(leftHandSymbolInfo)
End If
If container Is Nothing AndAlso Not couldBeMergedNamespace Then
Return ImmutableArray(Of ISymbol).Empty
End If
' We don't provide any member from System.Void (which is valid only in the context of typeof operation).
' Try to bail early to avoid unnecessary work even though compiler will handle this case for us.
If container IsNot Nothing AndAlso container.Kind = SymbolKind.NamedType Then
Dim typeContainer = DirectCast(container, INamedTypeSymbol)
If typeContainer.IsSystemVoid() Then
Return ImmutableArray(Of ISymbol).Empty
End If
End If
Debug.Assert((Not excludeInstance OrElse Not excludeShared) OrElse
(inNameOfExpression AndAlso Not excludeInstance AndAlso Not excludeShared))
Debug.Assert(Not excludeInstance OrElse Not useBaseReferenceAccessibility)
' On null conditional access, members of T for a Nullable(Of T) should be recommended
Dim unwrapNullable = _context.TargetToken.GetPreviousToken().IsKind(SyntaxKind.QuestionToken)
' No completion on types/namespace after conditional access
If leftExpression.Parent.IsKind(SyntaxKind.ConditionalAccessExpression) AndAlso
(couldBeMergedNamespace OrElse leftHandSymbolInfo.GetBestOrAllSymbols().FirstOrDefault().MatchesKind(SymbolKind.NamedType, SymbolKind.Namespace, SymbolKind.Alias)) Then
Return ImmutableArray(Of ISymbol).Empty
End If
Dim position = node.SpanStart
Dim symbols As ImmutableArray(Of ISymbol)
If couldBeMergedNamespace Then
symbols = leftHandSymbolInfo.CandidateSymbols _
.OfType(Of INamespaceSymbol) _
.SelectMany(Function(n) LookupSymbolsInContainer(n, position, excludeInstance)) _
.ToImmutableArray()
Else
symbols = GetMemberSymbols(container, position, excludeInstance, useBaseReferenceAccessibility, unwrapNullable, isForDereference:=False)
End If
If excludeShared Then
symbols = symbols.WhereAsArray(Function(s) Not s.IsShared)
End If
' If the left expression is Me, MyBase or MyClass and we're the first statement of constructor,
' we should filter out the parenting constructor. Otherwise, we should filter out all constructors.
If leftExpression.IsMeMyBaseOrMyClass() AndAlso node.IsFirstStatementInCtor() Then
Dim parentingCtor = GetEnclosingCtor(node)
Debug.Assert(parentingCtor IsNot Nothing)
symbols = symbols.WhereAsArray(Function(s) Not s.Equals(parentingCtor))
Else
symbols = symbols.WhereAsArray(Function(s) Not s.IsConstructor())
End If
' If the left expression is My.MyForms, we should filter out all non-property symbols
If leftHandSymbolInfo.Symbol IsNot Nothing AndAlso
leftHandSymbolInfo.Symbol.IsMyFormsProperty(_context.SemanticModel.Compilation) Then
symbols = symbols.WhereAsArray(Function(s) s.Kind = SymbolKind.Property)
End If
' Also filter out operators
symbols = symbols.WhereAsArray(Function(s) s.Kind <> SymbolKind.Method OrElse DirectCast(s, IMethodSymbol).MethodKind <> MethodKind.UserDefinedOperator)
' Filter events and generated members
symbols = symbols.WhereAsArray(Function(s) FilterEventsAndGeneratedSymbols(node, s))
' Never show the enum backing field
symbols = symbols.WhereAsArray(Function(s) s.Kind <> SymbolKind.Field OrElse Not s.ContainingType.IsEnumType() OrElse s.Name <> WellKnownMemberNames.EnumBackingFieldName)
Return symbols
End Function
Private Shared Function ContainsNamespaceCandidateSymbols(symbolInfo As SymbolInfo) As Boolean
Return symbolInfo.CandidateSymbols.Any() AndAlso symbolInfo.CandidateSymbols.All(Function(s) s.IsNamespace())
End Function
''' <summary>
''' In MemberAccessExpression Contexts, filter out event symbols (except for NameOf context), except inside AddRemoveHandler Statements
''' Also, filter out any implicitly declared members generated by event declaration or property declaration
''' </summary>
Private Function FilterEventsAndGeneratedSymbols(node As MemberAccessExpressionSyntax, s As ISymbol) As Boolean
If s.Kind = SymbolKind.Event AndAlso Not _context.IsNameOfContext Then
Return node IsNot Nothing AndAlso node.GetAncestor(Of AddRemoveHandlerStatementSyntax) IsNot Nothing
ElseIf s.Kind = SymbolKind.Field AndAlso s.IsImplicitlyDeclared Then
Dim associatedSymbol = DirectCast(s, IFieldSymbol).AssociatedSymbol
If associatedSymbol IsNot Nothing Then
Return associatedSymbol.Kind <> SymbolKind.Event AndAlso associatedSymbol.Kind <> SymbolKind.Property
End If
ElseIf s.Kind = SymbolKind.NamedType AndAlso s.IsImplicitlyDeclared Then
Return Not TypeOf DirectCast(s, INamedTypeSymbol).AssociatedSymbol Is IEventSymbol
End If
Return True
End Function
Private Function GetEnclosingCtor(node As MemberAccessExpressionSyntax) As IMethodSymbol
Dim symbol = _context.SemanticModel.GetEnclosingSymbol(node.SpanStart, _cancellationToken)
While symbol IsNot Nothing
Dim method = TryCast(symbol, IMethodSymbol)
If method IsNot Nothing AndAlso method.MethodKind = MethodKind.Constructor Then
Return method
End If
End While
Return Nothing
End Function
Private Function FilterToValidAccessibleSymbols(symbols As ImmutableArray(Of ISymbol)) As ImmutableArray(Of ISymbol)
' If this is an Inherits or Implements statement, we filter out symbols which do not recursively contain accessible, valid types.
Dim inheritsContext = IsInheritsStatementContext(_context.TargetToken)
Dim implementsContext = IsImplementsStatementContext(_context.TargetToken)
If inheritsContext OrElse implementsContext Then
Dim typeBlock = _context.TargetToken.Parent?.FirstAncestorOrSelf(Of TypeBlockSyntax)()
If typeBlock IsNot Nothing Then
Dim typeOrAssemblySymbol As ISymbol = _context.SemanticModel.GetDeclaredSymbol(typeBlock, _cancellationToken)
If typeOrAssemblySymbol Is Nothing Then
typeOrAssemblySymbol = _context.SemanticModel.Compilation.Assembly
End If
Dim isInterface = TryCast(typeOrAssemblySymbol, ITypeSymbol)?.TypeKind = TypeKind.Interface
If inheritsContext Then
' In an interface's Inherits statement, only show interfaces.
If isInterface Then
Return symbols.WhereAsArray(Function(s) IsValidAccessibleInterfaceOrContainer(s, typeOrAssemblySymbol))
End If
Return symbols.WhereAsArray(Function(s) IsValidAccessibleClassOrContainer(s, typeOrAssemblySymbol))
Else ' implementsContext
' In an interface's Implements statement, show nothing.
If isInterface Then
Return ImmutableArray(Of ISymbol).Empty
End If
Return symbols.WhereAsArray(Function(s) IsValidAccessibleInterfaceOrContainer(s, typeOrAssemblySymbol))
End If
End If
End If
Return symbols
End Function
Private Shared Function IsInheritsStatementContext(token As SyntaxToken) As Boolean
If token.IsChildToken(Of InheritsStatementSyntax)(Function(n) n.InheritsKeyword) Then
Return True
End If
Return token.IsChildToken(Of QualifiedNameSyntax)(Function(n) n.DotToken) AndAlso
token.Parent?.FirstAncestorOrSelf(Of InheritsStatementSyntax) IsNot Nothing
End Function
Private Shared Function IsImplementsStatementContext(token As SyntaxToken) As Boolean
If token.IsChildToken(Of ImplementsStatementSyntax)(Function(n) n.ImplementsKeyword) Then
Return True
End If
Return token.IsChildToken(Of QualifiedNameSyntax)(Function(n) n.DotToken) AndAlso
token.Parent?.FirstAncestorOrSelf(Of ImplementsStatementSyntax) IsNot Nothing
End Function
Private Function IsValidAccessibleInterfaceOrContainer(symbol As ISymbol, within As ISymbol) As Boolean
If symbol.Kind = SymbolKind.Alias Then
symbol = DirectCast(symbol, IAliasSymbol).Target
End If
Dim namespaceSymbol = TryCast(symbol, INamespaceSymbol)
If namespaceSymbol IsNot Nothing Then
Return namespaceSymbol.GetMembers().Any(Function(m) IsValidAccessibleInterfaceOrContainer(m, within))
End If
Dim namedTypeSymbol = TryCast(symbol, INamedTypeSymbol)
If namedTypeSymbol Is Nothing Then
Return False
End If
Return namedTypeSymbol.TypeKind = TypeKind.Interface OrElse
namedTypeSymbol _
.GetAccessibleMembersInThisAndBaseTypes(Of INamedTypeSymbol)(within) _
.Any(Function(m) IsOrContainsValidAccessibleInterface(m, within))
End Function
Private Function IsOrContainsValidAccessibleInterface(namespaceOrTypeSymbol As INamespaceOrTypeSymbol, within As ISymbol) As Boolean
If namespaceOrTypeSymbol.Kind = SymbolKind.Namespace Then
Return IsValidAccessibleInterfaceOrContainer(namespaceOrTypeSymbol, within)
End If
Dim namedTypeSymbol = TryCast(namespaceOrTypeSymbol, INamedTypeSymbol)
If namedTypeSymbol Is Nothing Then
Return False
End If
If namedTypeSymbol.TypeKind = TypeKind.Interface Then
Return True
End If
Return namedTypeSymbol.GetMembers() _
.OfType(Of INamedTypeSymbol)() _
.Where(Function(m) m.IsAccessibleWithin(within)) _
.Any(Function(m) IsOrContainsValidAccessibleInterface(m, within))
End Function
Private Function IsValidAccessibleClassOrContainer(symbol As ISymbol, within As ISymbol) As Boolean
If symbol.Kind = SymbolKind.Alias Then
symbol = DirectCast(symbol, IAliasSymbol).Target
End If
Dim type = TryCast(symbol, ITypeSymbol)
If type IsNot Nothing Then
If type.TypeKind = TypeKind.Class AndAlso Not type.IsSealed AndAlso Not Equals(type, within) Then
Return True
End If
If type.TypeKind = TypeKind.Class OrElse
type.TypeKind = TypeKind.Module OrElse
type.TypeKind = TypeKind.Struct Then
Return type.GetAccessibleMembersInThisAndBaseTypes(Of INamedTypeSymbol)(within).Any(Function(m) IsOrContainsValidAccessibleClass(m, within))
End If
End If
Dim namespaceSymbol = TryCast(symbol, INamespaceSymbol)
If namespaceSymbol IsNot Nothing Then
Return namespaceSymbol.GetMembers().Any(Function(m) IsValidAccessibleClassOrContainer(m, within))
End If
Return False
End Function
Private Function IsOrContainsValidAccessibleClass(namespaceOrTypeSymbol As INamespaceOrTypeSymbol, within As ISymbol) As Boolean
If namespaceOrTypeSymbol.Kind = SymbolKind.Namespace Then
Return IsValidAccessibleClassOrContainer(namespaceOrTypeSymbol, within)
End If
Dim namedTypeSymbol = TryCast(namespaceOrTypeSymbol, INamedTypeSymbol)
If namedTypeSymbol Is Nothing Then
Return False
End If
If namedTypeSymbol.TypeKind = TypeKind.Class AndAlso Not namedTypeSymbol.IsSealed AndAlso Not Equals(namedTypeSymbol, within) Then
Return True
End If
Return namedTypeSymbol.GetMembers() _
.OfType(Of INamedTypeSymbol)() _
.Where(Function(m) m.IsAccessibleWithin(within)) _
.Any(Function(m) IsOrContainsValidAccessibleClass(m, within))
End Function
End Class
End Class
End Namespace
|