/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Workspaces/VisualBasicTest/CodeGeneration/AddImportsTests.vb
1 062 строки
26 KB
Cyrus Najmabadi
Organize VB imports
27 июн 2025, 21:53
27 июн 2025, 21:53
e9c47fd
Код
Авторство
О чём код?
' 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.Threading Imports Microsoft.CodeAnalysis.AddImport Imports Microsoft.CodeAnalysis.Editing Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Simplification Imports Microsoft.CodeAnalysis.Test.Utilities Imports Microsoft.CodeAnalysis.VisualBasic.Formatting Imports Microsoft.CodeAnalysis.VisualBasic.Simplification Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Roslyn.Test.Utilities Imports Xunit Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Editting <[UseExportProvider]> Public Class AddImportsTests Private Const ExtensionAttributeSource = "Namespace System.Runtime.CompilerServices <AttributeUsage(AttributeTargets.Method Or AttributeTargets.[Class] Or AttributeTargets.Assembly)> Public NotInheritable Class ExtensionAttribute Inherits Attribute End Class End NameSpace" Private Shared Async Function GetDocument(code As String, withAnnotations As Boolean, Optional globalImports As String() = Nothing) As Task(Of Document) Dim ws As AdhocWorkspace = New AdhocWorkspace() Dim project As Project = ws.AddProject( ProjectInfo.Create( ProjectId.CreateNewId(), VersionStamp.Default, "test", "test.dll", LanguageNames.VisualBasic, metadataReferences:={NetFramework.mscorlib})) If globalImports IsNot Nothing Then Dim gi = GlobalImport.Parse(globalImports) project = project.WithCompilationOptions(DirectCast(project.CompilationOptions, VisualBasicCompilationOptions).WithGlobalImports(gi)) End If Dim extensionAttributeDoc = project.AddDocument("ExtensionAttribute.vb", ExtensionAttributeSource) Dim doc = extensionAttributeDoc.Project.AddDocument("test.vb", code) If withAnnotations Then Dim root = Await doc.GetSyntaxRootAsync() Dim model = Await doc.GetSemanticModelAsync() root = root.ReplaceNodes(root.DescendantNodesAndSelf().OfType(Of TypeSyntax)(), Function(o, c) Dim symbol = model.GetSymbolInfo(o).Symbol If symbol IsNot Nothing Then Return c.WithAdditionalAnnotations(SymbolAnnotation.Create(symbol), Simplifier.Annotation) End If Return c End Function) doc = doc.WithSyntaxRoot(root) End If Return doc End Function Private Shared Function TestNoImportsAddedAsync( initialText As String, useSymbolAnnotations As Boolean, Optional placeSystemNamespaceFirst As Boolean = False, Optional globalImports As String() = Nothing) As Task Return TestAsync(initialText, initialText, initialText, useSymbolAnnotations, placeSystemNamespaceFirst, globalImports, performCheck:=False) End Function Private Shared Async Function TestAsync( initialText As String, importsAddedText As String, simplifiedText As String, useSymbolAnnotations As Boolean, Optional placeSystemNamespaceFirst As Boolean = True, Optional globalImports As String() = Nothing, Optional performCheck As Boolean = True) As Task Dim doc = Await GetDocument(initialText, useSymbolAnnotations, globalImports) Dim addImportOptions = New AddImportPlacementOptions() With { .PlaceSystemNamespaceFirst = placeSystemNamespaceFirst } Dim formattingOptions = VisualBasicSyntaxFormattingOptions.Default Dim simplifierOptions = VisualBasicSimplifierOptions.Default Dim imported = If( useSymbolAnnotations, Await ImportAdder.AddImportsFromSymbolAnnotationAsync(doc, addImportOptions, CancellationToken.None), Await ImportAdder.AddImportsFromSyntaxesAsync(doc, addImportOptions, CancellationToken.None)) If importsAddedText IsNot Nothing Then Dim formatted = Await Formatter.FormatAsync(imported, SyntaxAnnotation.ElasticAnnotation, formattingOptions, CancellationToken.None) Dim actualText = (Await formatted.GetTextAsync()).ToString() Assert.Equal(importsAddedText, actualText) End If If simplifiedText IsNot Nothing Then Dim reduced = Await Simplifier.ReduceAsync(imported, simplifierOptions, CancellationToken.None) Dim formatted = Await Formatter.FormatAsync(reduced, SyntaxAnnotation.ElasticAnnotation, formattingOptions, CancellationToken.None) Dim actualText = (Await formatted.GetTextAsync()).ToString() AssertEx.EqualOrDiff(simplifiedText, actualText) End If If performCheck Then If initialText = importsAddedText AndAlso importsAddedText = simplifiedText Then Throw New Exception($"use {NameOf(TestNoImportsAddedAsync)}") End If End If End Function Public Shared TestAllData As Object()() = { New Object() {False}, New Object() {True} } <Theory, MemberData(NameOf(TestAllData))> Public Async Function TestAddImport(useSymbolAnnotations As Boolean) As Task Await TestAsync( "Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Imports System.Collections.Generic Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Imports System.Collections.Generic Class C Public F As List(Of Integer) End Class", useSymbolAnnotations) End Function <Theory, MemberData(NameOf(TestAllData))> Public Async Function TestAddSystemImportFirst(useSymbolAnnotations As Boolean) As Task Await TestAsync( "Imports N Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Imports System.Collections.Generic Imports N Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Imports System.Collections.Generic Imports N Class C Public F As List(Of Integer) End Class", useSymbolAnnotations) End Function <Theory, MemberData(NameOf(TestAllData))> Public Async Function TestDoNotAddSystemImportFirst(useSymbolAnnotations As Boolean) As Task Await TestAsync( "Imports N Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Imports N Imports System.Collections.Generic Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Imports N Imports System.Collections.Generic Class C Public F As List(Of Integer) End Class", useSymbolAnnotations, placeSystemNamespaceFirst:=False) End Function <Theory, MemberData(NameOf(TestAllData))> Public Async Function TestAddImportsInOrder(useSymbolAnnotations As Boolean) As Task Await TestAsync( "Imports System.Collections Imports System.Diagnostics Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Imports System.Collections Imports System.Collections.Generic Imports System.Diagnostics Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Imports System.Collections Imports System.Collections.Generic Imports System.Diagnostics Class C Public F As List(Of Integer) End Class", useSymbolAnnotations) End Function <Theory, MemberData(NameOf(TestAllData))> Public Async Function TestAddMultipleImportsInOrder(useSymbolAnnotations As Boolean) As Task Await TestAsync( "Imports System.Collections Imports System.Diagnostics Class C Public F As System.Collections.Generic.List(Of Integer) Public Handler As System.EventHandler End Class", "Imports System Imports System.Collections Imports System.Collections.Generic Imports System.Diagnostics Class C Public F As System.Collections.Generic.List(Of Integer) Public Handler As System.EventHandler End Class", "Imports System Imports System.Collections Imports System.Collections.Generic Imports System.Diagnostics Class C Public F As List(Of Integer) Public Handler As EventHandler End Class", useSymbolAnnotations) End Function <Theory, MemberData(NameOf(TestAllData))> Public Async Function TestImportNotAddedAgainIfAlreadyExists(useSymbolAnnotations As Boolean) As Task Await TestAsync( "Imports System.Collections.Generic Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Imports System.Collections.Generic Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Imports System.Collections.Generic Class C Public F As List(Of Integer) End Class", useSymbolAnnotations) End Function <Fact> Public Async Function TestBuiltInTypeFromSyntaxes() As Task Await TestAsync( "Class C Public F As System.Int32 End Class", "Imports System Class C Public F As System.Int32 End Class", " Class C Public F As Integer End Class", useSymbolAnnotations:=False) End Function <Fact> Public Async Function TestBuiltInTypeFromSymbols() As Task Await TestAsync( "Class C Public F As System.Int32 End Class", "Class C Public F As System.Int32 End Class", "Class C Public F As Integer End Class", useSymbolAnnotations:=True) End Function <Theory, MemberData(NameOf(TestAllData))> Public Async Function TestImportNotAddedIfGloballyImported(useSymbolAnnotations As Boolean) As Task Await TestAsync( "Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Class C Public F As List(Of Integer) End Class", useSymbolAnnotations, globalImports:={"System.Collections.Generic"}) End Function <Theory, MemberData(NameOf(TestAllData))> Public Async Function TestImportNotAddedForNamespaceDeclarations(useSymbolAnnotations As Boolean) As Task Await TestNoImportsAddedAsync( "Namespace N End Namespace", useSymbolAnnotations) End Function <Theory, MemberData(NameOf(TestAllData))> Public Async Function TestImportAddedAndRemovedForReferencesInsideNamespaceDeclarations(useSymbolAnnotations As Boolean) As Task Await TestAsync( "Namespace N Class C Private _c As N.C End Class End Namespace", "Namespace N Class C Private _c As N.C End Class End Namespace", "Namespace N Class C Private _c As C End Class End Namespace", useSymbolAnnotations) End Function <Theory, MemberData(NameOf(TestAllData))> Public Async Function TestRemoveImportIfItMakesReferencesAmbiguous(useSymbolAnnotations As Boolean) As Task ' this is not really an artifact of the AddImports feature, it is due ' to Simplifier not reducing the namespace reference because it would ' become ambiguous, thus leaving an unused imports statement Await TestAsync( "Namespace N Class C End Class End Namespace Class C Private F As N.C End Class ", "Imports N Namespace N Class C End Class End Namespace Class C Private F As N.C End Class ", " Namespace N Class C End Class End Namespace Class C Private F As N.C End Class ", useSymbolAnnotations) End Function <Theory, MemberData(NameOf(TestAllData))> Public Async Function TestPartialNamespacesNotUsed(useSymbolAnnotations As Boolean) As Task Await TestAsync( "Imports System.Collections Public Class C Public F1 As ArrayList Public F2 As System.Collections.Generic.List(Of Integer) End Class", "Imports System.Collections Imports System.Collections.Generic Public Class C Public F1 As ArrayList Public F2 As System.Collections.Generic.List(Of Integer) End Class", "Imports System.Collections Imports System.Collections.Generic Public Class C Public F1 As ArrayList Public F2 As List(Of Integer) End Class", useSymbolAnnotations) End Function <Theory, MemberData(NameOf(TestAllData))> Public Async Function TestDoNotAddImportWithExisitingImportDifferentCase(useSymbolAnnotations As Boolean) As Task Await TestAsync( "Imports system.collections.generic Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Imports system.collections.generic Class C Public F As System.Collections.Generic.List(Of Integer) End Class", "Imports system.collections.generic Class C Public F As List(Of Integer) End Class", useSymbolAnnotations) End Function #Region "AddImports Safe Tests" <Fact> Public Async Function TestSafeWithMatchingSimpleName() As Task Await TestNoImportsAddedAsync( "Imports B Namespace A Class C1 End Class Class C2 End Class End Namespace Namespace B Class C1 End Class End Namespace Class C Private Function M(ByVal c2 As A.C2) As C1 Return Nothing End Function End Class", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingSimpleName_CaseInsensitive() As Task Await TestNoImportsAddedAsync( "Imports B Namespace A Class c1 End Class Class C2 End Class End Namespace Namespace B Class C1 End Class End Namespace Class C Private Function M(ByVal c2 As A.C2) As C1 Return Nothing End Function End Class", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingSimpleNameDifferentCase() As Task Await TestNoImportsAddedAsync( "Imports B Namespace A Class C1 End Class Class C2 End Class End Namespace Namespace B Class C1 End Class End Namespace Class C Private Function M(ByVal c2 As A.C2) As c1 Return Nothing End Function End Class", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingGenericName() As Task Await TestNoImportsAddedAsync( "Imports B Namespace A Class C1(Of T) End Class Class C2 End Class End Namespace Namespace B Class C1(Of T) End Class End Namespace Class C Private Function M(ByVal c2 As A.C2) As C1(Of Integer) Return Nothing End Function End Class", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingGenericName_DifferentArity() As Task Await TestAsync( "Imports B Namespace A Class C1(Of T, X) End Class Class C2 End Class End Namespace Namespace B Class C1(Of T) End Class End Namespace Class C Private Function M(ByVal c2 As A.C2) As C1(Of Integer) Return Nothing End Function End Class", "Imports A Imports B Namespace A Class C1(Of T, X) End Class Class C2 End Class End Namespace Namespace B Class C1(Of T) End Class End Namespace Class C Private Function M(ByVal c2 As A.C2) As C1(Of Integer) Return Nothing End Function End Class", "Imports A Imports B Namespace A Class C1(Of T, X) End Class Class C2 End Class End Namespace Namespace B Class C1(Of T) End Class End Namespace Class C Private Function M(ByVal c2 As C2) As C1(Of Integer) Return Nothing End Function End Class", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingQualifiedName() As Task Await TestNoImportsAddedAsync( "Imports B Namespace A Class O End Class Class C2 End Class End Namespace Namespace B Class O Public Class C1 End Class End Class End Namespace Class C Private Function M(ByVal c2 As A.C2) As O.C1 Return Nothing End Function End Class", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingAliasedIdentifierName() As Task Await TestNoImportsAddedAsync( "Imports C1 = B.C1 Namespace A Class C1 End Class Class C2 End Class End Namespace Namespace B Class C1 End Class End Namespace Namespace Inner Class C Private Function M(ByVal c2 As A.C2) As C1 Return Nothing End Function End Class End Namespace", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingGenericNameAndTypeArguments() As Task Await TestNoImportsAddedAsync( "Imports B Namespace A Class C1(Of T) End Class Class C2 End Class Class C3 End Class End Namespace Namespace B Class C1(Of T) End Class Class C3 End Class End Namespace Class C Private Function M(ByVal c2 As A.C2) As C1(Of C3) Return Nothing End Function End Class", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingQualifiedNameAndTypeArguments() As Task Await TestNoImportsAddedAsync( "Imports B Namespace A Class O End Class Class C2 End Class Class C3 End Class End Namespace Namespace B Class C3 End Class Class O Public Class C1(Of T) End Class End Class End Namespace Class C Private Function M(ByVal c2 As A.C2) As O.C1(Of C3) Return Nothing End Function End Class", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingSimpleNameInAllLocations() As Task Await TestNoImportsAddedAsync( "Imports B Imports System.Collections.Generic Namespace A Class C1 End Class Class C2 End Class End Namespace Namespace B Class C1 Public Shared ReadOnly Property P As C1 End Class End Namespace Class C ''' <summary> ''' <see cref=""C1""/> ''' </summary> Private Function M(ByVal c As C1, ByVal c2 As A.C2) As C1 Dim result As C1 result = DirectCast(c, C1) result = CType(c, C1) result = new C1() result = C1.P result = new C1(0){}(0) Dim list = New List(Of C1)() Dim tuple As (C1, Integer) = (Nothing, Nothing) Dim t = GetType(C1) Dim s = NameOf(C1) Return result End Function End Class", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingExtensionMethod() As Task Await TestNoImportsAddedAsync( "Imports B Imports System.Runtime.CompilerServices Namespace A Friend Module AExtensions <Extension()> Sub M(ByVal a As Integer) End Sub End Module Public Class C1 End Class End Namespace Namespace B Friend Module BExtensions <Extension()> Sub M(ByVal a As Integer) End Sub End Module End Namespace Friend Class C Private Sub M(ByVal c1 As A.C1) Call 42.M() End Sub End Class", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingExtensionMethod_CaseInsensitive() As Task Await TestNoImportsAddedAsync( "Imports B Imports System.Runtime.CompilerServices Namespace A Friend Module AExtensions <Extension()> Sub M(ByVal a As Integer) End Sub End Module Public Class C1 End Class End Namespace Namespace B Friend Module BExtensions <Extension()> Sub M(ByVal a As Integer) End Sub End Module End Namespace Friend Class C Private Sub M(ByVal c1 As A.C1) Call 42.m() End Sub End Class", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingExtensionMethodAndArguments() As Task Await TestNoImportsAddedAsync( "Imports B Imports System.Runtime.CompilerServices Namespace A Friend Module AExtensions <Extension()> Sub M(ByVal a As Integer, ByVal c2 As C2) End Sub End Module Public Class C1 End Class Public Class C2 End Class End Namespace Namespace B Friend Module BExtensions <Extension()> Sub M(ByVal a As Integer, ByVal c2 As C2) End Sub End Module Public Class C2 End Class End Namespace Friend Class C Private Sub M(ByVal c1 As A.C1) Call 42.M(New C2()) End Sub End Class", useSymbolAnnotations:=True) End Function <Fact> Public Async Function TestSafeWithMatchingExtensionMethodAndTypeArguments() As Task Await TestNoImportsAddedAsync( "Imports B Imports System.Runtime.CompilerServices Namespace A Friend Module AExtensions <Extension()> Sub M(Of T)(ByVal a As Integer) End Sub End Module Public Class C1 End Class Public Class C2 End Class End Namespace Namespace B Friend Module BExtensions <Extension()> Sub M(Of T)(ByVal a As Integer) End Sub End Module Public Class C2 End Class End Namespace Friend Class C Private Sub M(ByVal c1 As A.C1) Call 42.M(Of C2)() End Sub End Class", useSymbolAnnotations:=True) End Function ' <Theory, InlineData(True), InlineData(False)> ' Public Async Function TestWarnsWithMatchingExtensionMethodUsedAsDelegate(useSymbolAnnotations As Boolean) As Task ' Dim source = "Imports System 'Imports B 'Imports System.Runtime.CompilerServices 'Namespace A ' Friend Module AExtensions ' <Extension()> ' Sub M(ByVal a As Integer) ' End Sub ' End Module ' Public Class C1 ' End Class 'End Namespace 'Namespace B ' Friend Module BExtensions ' <Extension()> ' Sub M(ByVal a As Object) ' End Sub ' End Module 'End Namespace 'Friend Class C ' Private Function M(ByVal c1 As A.C1) As Action ' Return AddressOf 42.M ' End Function 'End Class" ' Await TestAsync( ' source, '"Imports System 'Imports B 'Imports System.Runtime.CompilerServices 'Imports A 'Namespace A ' Friend Module AExtensions ' <Extension()> ' Sub M(ByVal a As Integer) ' End Sub ' End Module ' Public Class C1 ' End Class 'End Namespace 'Namespace B ' Friend Module BExtensions ' <Extension()> ' Sub M(ByVal a As Object) ' End Sub ' End Module 'End Namespace 'Friend Class C ' Private Function M(ByVal c1 As A.C1) As Action ' Return AddressOf 42.M ' End Function 'End Class", '"Imports System 'Imports B 'Imports System.Runtime.CompilerServices 'Imports A 'Namespace A ' Friend Module AExtensions ' <Extension()> ' Sub M(ByVal a As Integer) ' End Sub ' End Module ' Public Class C1 ' End Class 'End Namespace 'Namespace B ' Friend Module BExtensions ' <Extension()> ' Sub M(ByVal a As Object) ' End Sub ' End Module 'End Namespace 'Friend Class C ' Private Function M(ByVal c1 As C1) As Action ' Return AddressOf 42.M ' End Function 'End Class", useSymbolAnnotations) ' Dim doc = Await GetDocument(source, useSymbolAnnotations) ' Dim options As OptionSet = Await doc.GetOptionsAsync() ' Dim imported = Await ImportAdder.AddImportsFromSyntaxesAsync(doc, True, options) ' Dim root = Await imported.GetSyntaxRootAsync() ' Dim nodeWithWarning = root.GetAnnotatedNodes(WarningAnnotation.Kind).Single() ' Assert.Equal("42.M" & vbCrLf, nodeWithWarning.ToFullString()) ' Dim warning = nodeWithWarning.GetAnnotations(WarningAnnotation.Kind).Single() ' Dim expectedWarningMessage = String.Format(WorkspacesResources.Warning_adding_imports_will_bring_an_extension_method_into_scope_with_the_same_name_as_member_access, "M") ' Assert.Equal(expectedWarningMessage, WarningAnnotation.GetDescription(warning)) ' End Function <Fact, WorkItem("https://github.com/dotnet/roslyn/issues/39592")> Public Async Function TestCanExpandCrefSignaturePart() As Task Await TestNoImportsAddedAsync( "Imports B Namespace A Class C1 End Class Class C2 End Class End Namespace Namespace B Class C1 End Class End Namespace Class C ''' <see cref=""M(C1)""/> Private Sub M(ByVal c2 As A.C2) End Sub Private Sub M(ByVal c1 As C1) End Sub End Class", useSymbolAnnotations:=True) End Function <Fact, WorkItem("https://github.com/dotnet/roslyn/issues/39592")> Public Async Function TestSafeWithLambdaExtensionMethodAmbiguity() As Task Await TestNoImportsAddedAsync( "Imports System Imports System.Runtime.CompilerServices Class C ' Don't add a using for N even though it is used here. Public x As N.Other Public Sub Main() M(Sub(x) x.M1()) End Sub Public Shared Sub M(a As Action(Of C)) End Sub Public Shared Sub M(a As Action(Of Integer)) End Sub Public Sub M1() End Sub End Class Namespace N Public Class Other End Class Public Module Extensions <Extension> Public Sub M1(a As Integer) End Sub End Module End Namespace", useSymbolAnnotations:=True) End Function #End Region End Class End Namespace