' 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.PooledObjects Imports Microsoft.CodeAnalysis.RemoveUnnecessaryImports Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports Friend NotInheritable Class VisualBasicUnnecessaryImportsProvider Inherits AbstractUnnecessaryImportsProvider(Of ImportsClauseSyntax) Private Const BC30561 As String = NameOf(BC30561) 'X' is ambiguous, imported from the namespaces or types... Private Const BC50000 As String = NameOf(BC50000) ' HDN_UnusedImportClause Private Const BC50001 As String = NameOf(BC50001) ' HDN_UnusedImportStatement Public Shared Instance As New VisualBasicUnnecessaryImportsProvider Private Sub New() End Sub Public Overrides Function GetUnnecessaryImports( model As SemanticModel, predicate As Func(Of SyntaxNode, Boolean), cancellationToken As CancellationToken) As ImmutableArray(Of ImportsClauseSyntax) Dim root = model.SyntaxTree.GetRoot(cancellationToken) predicate = If(predicate, Functions(Of SyntaxNode).True) Dim diagnostics = model.GetDiagnostics(cancellationToken:=cancellationToken) Dim unnecessaryImports = ArrayBuilder(Of ImportsClauseSyntax).GetInstance() For Each diagnostic In diagnostics If diagnostic.Id = BC50000 Then Dim node = TryCast(root.FindNode(diagnostic.Location.SourceSpan), ImportsClauseSyntax) If node IsNot Nothing AndAlso predicate(node) Then unnecessaryImports.Add(node) End If End If If diagnostic.Id = BC50001 Then Dim node = TryCast(root.FindNode(diagnostic.Location.SourceSpan), ImportsStatementSyntax) If node IsNot Nothing AndAlso predicate(node) Then unnecessaryImports.AddRange(node.ImportsClauses) End If End If Next ' Now, look for imports in the file that seem redundant because there is also the same project-level import. ' However, it may not be viable to remove these as its possible that these imports are necessary to prevent ' ambiguity warnings. Specifically, the local imports are examined first prior to looking up in the project ' imports. Dim redundantImports = ArrayBuilder(Of ImportsClauseSyntax).GetInstance() AddRedundantImports(DirectCast(root, CompilationUnitSyntax), model, redundantImports, predicate, cancellationToken) For Each redundantImport In redundantImports If unnecessaryImports.Contains(redundantImport) OrElse RemovalCausesAmbiguity(model, redundantImport, cancellationToken) Then Continue For End If unnecessaryImports.Add(redundantImport) Next unnecessaryImports.RemoveDuplicates() Return unnecessaryImports.ToImmutableArray() End Function Private Shared Function RemovalCausesAmbiguity(model As SemanticModel, redundantImport As ImportsClauseSyntax, cancellationToken As CancellationToken) As Boolean Dim root = DirectCast(model.SyntaxTree.GetRoot(cancellationToken), CompilationUnitSyntax) Dim updatedRoot = VisualBasicRemoveUnnecessaryImportsRewriter.RemoveUnnecessaryImports(root, redundantImport, cancellationToken) Dim updatedSyntaxTree = model.SyntaxTree.WithRootAndOptions(updatedRoot, model.SyntaxTree.Options) Dim updatedCompilation = model.Compilation.ReplaceSyntaxTree(model.SyntaxTree, updatedSyntaxTree) Dim updatedModel = updatedCompilation.GetSemanticModel(updatedSyntaxTree) Dim diagnostics = updatedModel.GetDiagnostics(cancellationToken:=cancellationToken) Return diagnostics.Any(Function(d) d.Id = BC30561) End Function Private Shared Sub AddRedundantImports( compilationUnit As CompilationUnitSyntax, semanticModel As SemanticModel, redundantImports As ArrayBuilder(Of ImportsClauseSyntax), predicate As Func(Of SyntaxNode, Boolean), cancellationToken As CancellationToken) ' Now that we've visited the tree, add any imports that bound to project level ' imports. We definitely can remove them. For Each statement In compilationUnit.Imports For Each clause In statement.ImportsClauses cancellationToken.ThrowIfCancellationRequested() Dim simpleImportsClause = TryCast(clause, SimpleImportsClauseSyntax) If simpleImportsClause IsNot Nothing Then If simpleImportsClause.Alias Is Nothing Then AddRedundantMemberImportsClause(simpleImportsClause, semanticModel, redundantImports, predicate, cancellationToken) Else AddRedundantAliasImportsClause(simpleImportsClause, semanticModel, redundantImports, predicate, cancellationToken) End If End If Next Next End Sub Private Shared Sub AddRedundantAliasImportsClause( clause As SimpleImportsClauseSyntax, semanticModel As SemanticModel, redundantImports As ArrayBuilder(Of ImportsClauseSyntax), predicate As Func(Of SyntaxNode, Boolean), cancellationToken As CancellationToken) Dim semanticInfo = semanticModel.GetSymbolInfo(clause.Name, cancellationToken) Dim namespaceOrType = TryCast(semanticInfo.Symbol, INamespaceOrTypeSymbol) If namespaceOrType Is Nothing Then Return End If Dim compilation = semanticModel.Compilation Dim aliasSymbol = compilation.AliasImports.FirstOrDefault(Function(a) a.Name = clause.Alias.Identifier.ValueText) If aliasSymbol IsNot Nothing AndAlso aliasSymbol.Target.Equals(semanticInfo.Symbol) AndAlso predicate(clause) Then redundantImports.Add(clause) End If End Sub Private Shared Sub AddRedundantMemberImportsClause( clause As SimpleImportsClauseSyntax, semanticModel As SemanticModel, redundantImports As ArrayBuilder(Of ImportsClauseSyntax), predicate As Func(Of SyntaxNode, Boolean), cancellationToken As CancellationToken) Dim semanticInfo = semanticModel.GetSymbolInfo(clause.Name, cancellationToken) Dim namespaceOrType = TryCast(semanticInfo.Symbol, INamespaceOrTypeSymbol) If namespaceOrType Is Nothing Then Return End If Dim compilation = semanticModel.Compilation If compilation.MemberImports.Contains(namespaceOrType) AndAlso predicate(clause) Then redundantImports.Add(clause) End If End Sub End Class End Namespace |