| File: Diagnostics\Analyzers\TypeSyntaxSimplifierWalker.vb | Web Access |
| Project: src\roslyn\src\Features\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.Features.vbproj (Microsoft.CodeAnalysis.VisualBasic.Features) |
' 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.Threading Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Shared.Collections Imports Microsoft.CodeAnalysis.VisualBasic.Simplification Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.SimplifyTypeNames Friend Class TypeSyntaxSimplifierWalker Inherits VisualBasicSyntaxWalker Private Shared ReadOnly s_emptyAliasedNames As ImmutableHashSet(Of String) = ImmutableHashSet.Create(Of String)(CaseInsensitiveComparison.Comparer) Private Shared ReadOnly s_predefinedTypeMetadataNames As ImmutableHashSet(Of String) = ImmutableHashSet.Create( CaseInsensitiveComparison.Comparer, NameOf([Boolean]), NameOf([SByte]), NameOf([Byte]), NameOf(Int16), NameOf(UInt16), NameOf(Int32), NameOf(UInt32), NameOf(Int64), NameOf(UInt64), NameOf([Single]), NameOf([Double]), NameOf([Decimal]), NameOf([String]), NameOf([Char]), NameOf(DateTime), NameOf([Object])) Private ReadOnly _analyzer As VisualBasicSimplifyTypeNamesDiagnosticAnalyzer Private ReadOnly _semanticModel As SemanticModel Private ReadOnly _options As VisualBasicSimplifierOptions Private ReadOnly _analyzerOptions As AnalyzerOptions Private ReadOnly _ignoredSpans As TextSpanMutableIntervalTree Private ReadOnly _cancellationToken As CancellationToken Private _diagnostics As ImmutableArray(Of Diagnostic).Builder ''' <summary> ''' Set of type and namespace names that have an alias associated with them. i.e. if the ''' user has <c>Imports X = System.DateTime</c>, then <c>DateTime</c> will be in this set. ''' This is used so we can easily tell if we should try to simplify some identifier to an ''' alias when we encounter it. ''' </summary> Private ReadOnly _aliasedNames As ImmutableHashSet(Of String) Public ReadOnly Property Diagnostics As ImmutableArray(Of Diagnostic) Get Return If(_diagnostics?.ToImmutable(), ImmutableArray(Of Diagnostic).Empty) End Get End Property Public ReadOnly Property DiagnosticsBuilder As ImmutableArray(Of Diagnostic).Builder Get If _diagnostics Is Nothing Then Interlocked.CompareExchange(_diagnostics, ImmutableArray.CreateBuilder(Of Diagnostic)(), Nothing) End If Return _diagnostics End Get End Property Public Sub New(analyzer As VisualBasicSimplifyTypeNamesDiagnosticAnalyzer, semanticModel As SemanticModel, options As VisualBasicSimplifierOptions, analyzerOptions As AnalyzerOptions, ignoredSpans As TextSpanMutableIntervalTree, cancellationToken As CancellationToken) MyBase.New(SyntaxWalkerDepth.StructuredTrivia) _analyzer = analyzer _semanticModel = semanticModel _options = options _analyzerOptions = analyzerOptions _ignoredSpans = ignoredSpans _cancellationToken = cancellationToken Dim root = semanticModel.SyntaxTree.GetRoot(cancellationToken) _aliasedNames = GetAliasedNames(TryCast(root, CompilationUnitSyntax)) For Each aliasSymbol In semanticModel.Compilation.AliasImports() _aliasedNames = _aliasedNames.Add(aliasSymbol.Target.Name) Next End Sub Private Shared Function GetAliasedNames(compilationUnit As CompilationUnitSyntax) As ImmutableHashSet(Of String) Dim aliasedNames = s_emptyAliasedNames If compilationUnit Is Nothing Then Return aliasedNames End If For Each importsStatement In compilationUnit.Imports For Each importsClause In importsStatement.ImportsClauses Dim simpleImportsClause = TryCast(importsClause, SimpleImportsClauseSyntax) If simpleImportsClause Is Nothing Then Continue For End If AddAliasedName(aliasedNames, simpleImportsClause) Next Next Return aliasedNames End Function Private Shared Sub AddAliasedName(ByRef aliasedNames As ImmutableHashSet(Of String), simpleImportsClause As SimpleImportsClauseSyntax) If simpleImportsClause.Alias IsNot Nothing Then Dim identifierName = TryCast(simpleImportsClause.Name.GetRightmostName(), IdentifierNameSyntax) If identifierName IsNot Nothing Then If Not String.IsNullOrEmpty(identifierName.Identifier.ValueText) Then aliasedNames = aliasedNames.Add(identifierName.Identifier.ValueText) End If End If End If End Sub Public Overrides Sub VisitQualifiedName(node As QualifiedNameSyntax) If _ignoredSpans IsNot Nothing AndAlso _ignoredSpans.HasIntervalThatOverlapsWith(node.FullSpan.Start, node.FullSpan.Length) Then Return End If If node.IsKind(SyntaxKind.QualifiedName) AndAlso TrySimplify(node) Then Return End If MyBase.VisitQualifiedName(node) End Sub Public Overrides Sub VisitMemberAccessExpression(node As MemberAccessExpressionSyntax) If _ignoredSpans IsNot Nothing AndAlso _ignoredSpans.HasIntervalThatOverlapsWith(node.FullSpan.Start, node.FullSpan.Length) Then Return End If If node.IsKind(SyntaxKind.SimpleMemberAccessExpression) AndAlso TrySimplify(node) Then Return End If MyBase.VisitMemberAccessExpression(node) End Sub Public Overrides Sub VisitIdentifierName(node As IdentifierNameSyntax) If _ignoredSpans IsNot Nothing AndAlso _ignoredSpans.HasIntervalThatOverlapsWith(node.FullSpan.Start, node.FullSpan.Length) Then Return End If ' Always try to simplify identifiers with an 'Attribute' suffix. ' ' In other cases, don't bother looking at the right side of A.B or A!B. We will process those in ' one of our other top level Visit methods (Like VisitQualifiedName). Dim canTrySimplify = CaseInsensitiveComparison.EndsWith(node.Identifier.ValueText, "Attribute") If Not canTrySimplify AndAlso Not node.IsRightSideOfDotOrBang() Then ' The only possible simplifications to an unqualified identifier are replacement with an alias or ' replacement with a predefined type. canTrySimplify = CanReplaceIdentifierWithAlias(node.Identifier.ValueText) _ OrElse CanReplaceIdentifierWithPredefinedType(node.Identifier.ValueText) End If If canTrySimplify AndAlso TrySimplify(node) Then Return End If MyBase.VisitIdentifierName(node) End Sub Private Function CanReplaceIdentifierWithAlias(identifier As String) As Boolean Return _aliasedNames.Contains(identifier) End Function Private Shared Function CanReplaceIdentifierWithPredefinedType(identifier As String) As Boolean Return s_predefinedTypeMetadataNames.Contains(identifier) End Function Public Overrides Sub VisitGenericName(node As GenericNameSyntax) If _ignoredSpans IsNot Nothing AndAlso _ignoredSpans.HasIntervalThatOverlapsWith(node.FullSpan.Start, node.FullSpan.Length) Then Return End If If node.IsKind(SyntaxKind.GenericName) AndAlso TrySimplify(node) Then Return End If MyBase.VisitGenericName(node) End Sub Private Function TrySimplify(node As SyntaxNode) As Boolean Dim diagnostic As Diagnostic = Nothing If Not _analyzer.TrySimplify(_semanticModel, node, diagnostic, _options, _analyzerOptions, _cancellationToken) Then Return False End If DiagnosticsBuilder.Add(diagnostic) Return True End Function End Class End Namespace