|
' 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.Test.Utilities
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Roslyn.Test.Utilities
Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests
Public Class GetDiagnosticsTests
Inherits BasicTestBase
<Fact>
Public Sub DiagnosticsFilteredInMethodBody()
Dim source = <project><file>
Class C
Sub S()
@
#
!
End Sub
End Class
</file></project>
Dim compilation = CreateCompilationWithMscorlib40(source)
Dim model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single())
' CreateCompilation... method normalizes EOL chars of xml literals,
' so we cannot verify against spans in the original xml here as positions would differ
Dim sourceText = compilation.SyntaxTrees.Single().GetText().ToString()
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "(?s)^.*$", "BC30035", "BC30248", "BC30203", "BC30157")
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "@", "BC30035")
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "#", "BC30248")
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "(?<=\!)", "BC30203", "BC30157")
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "!", "BC30203", "BC30157")
End Sub
<Fact>
Public Sub DiagnosticsFilteredInMethodBodyInsideNamespace()
Dim source = <project><file>
Namespace N
Class C
Sub S()
X
End Sub
End Class
End Namespace
Class D
ReadOnly Property P As Integer
Get
Return Y
End Get
End Property
End Class
</file></project>
Dim compilation = CreateCompilationWithMscorlib40(source)
Dim model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single())
Dim sourceText = compilation.SyntaxTrees.Single().GetText().ToString()
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "X", "BC30451")
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "Y", "BC30451")
End Sub
<Fact>
Public Sub DiagnosticsFilteredForIntersectingIntervals()
Dim source = <project><file>
Class C
Inherits Abracadabra
End Class
</file></project>
Dim compilation = CreateCompilationWithMscorlib40(source)
Dim model = compilation.GetSemanticModel(compilation.SyntaxTrees.Single())
' CreateCompilation... method normalizes EOL chars of xml literals,
' so we cannot verify against spans in the original xml here as positions would differ
Dim sourceText = compilation.SyntaxTrees.Single().GetText().ToString()
Const ErrorId = "BC30002"
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "(?s)^.*$", ErrorId)
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "Abracadabra", ErrorId)
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "ts Abracadabra", ErrorId)
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "ts Abracadabr", ErrorId)
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "Abracadabra[\r\n]+", ErrorId)
DiagnosticsHelper.VerifyDiagnostics(model, sourceText, "bracadabra[\r\n]+", ErrorId)
End Sub
<Fact, WorkItem(1066483, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1066483")>
Public Sub TestDiagnosticWithSeverity()
Dim source = <project><file>
Class C
Sub Goo()
Dim x
End Sub
End Class
</file></project>
Dim compilation = CreateCompilationWithMscorlib40(source)
Dim diag = compilation.GetDiagnostics().Single()
Assert.Equal(DiagnosticSeverity.Warning, diag.Severity)
Assert.Equal(1, diag.WarningLevel)
Dim [error] = diag.WithSeverity(DiagnosticSeverity.Error)
Assert.Equal(DiagnosticSeverity.Error, [error].Severity)
Assert.Equal(DiagnosticSeverity.Warning, [error].DefaultSeverity)
Assert.Equal(0, [error].WarningLevel)
Dim warning = [error].WithSeverity(DiagnosticSeverity.Warning)
Assert.Equal(DiagnosticSeverity.Warning, warning.Severity)
Assert.Equal(DiagnosticSeverity.Warning, warning.DefaultSeverity)
Assert.Equal(1, warning.WarningLevel)
Dim hidden = warning.WithSeverity(DiagnosticSeverity.Hidden)
Assert.Equal(DiagnosticSeverity.Hidden, hidden.Severity)
Assert.Equal(DiagnosticSeverity.Warning, hidden.DefaultSeverity)
Assert.Equal(1, hidden.WarningLevel)
Dim info = warning.WithSeverity(DiagnosticSeverity.Info)
Assert.Equal(DiagnosticSeverity.Info, info.Severity)
Assert.Equal(DiagnosticSeverity.Warning, info.DefaultSeverity)
Assert.Equal(1, info.WarningLevel)
End Sub
<Fact, WorkItem(7446, "https://github.com/dotnet/roslyn/issues/7446")>
Public Sub TestCompilationEventQueueWithSemanticModelGetDiagnostics()
Dim source1 = <file>
Namespace N1
Partial Class C
Private Sub NonPartialMethod1()
End Sub
End Class
End Namespace
</file>.Value
Dim source2 =
<file>
Namespace N1
Partial Class C
Private Sub NonPartialMethod2()
End Sub
End Class
End Namespace
</file>.Value
Dim tree1 = VisualBasicSyntaxTree.ParseText(source1, path:="file1")
Dim tree2 = VisualBasicSyntaxTree.ParseText(source2, path:="file2")
Dim eventQueue = New AsyncQueue(Of CompilationEvent)()
Dim compilation = CreateCompilationWithMscorlib461({tree1, tree2}).WithEventQueue(eventQueue)
' Invoke SemanticModel.GetDiagnostics to force populate the event queue for symbols in the first source file.
Dim tree = compilation.SyntaxTrees.[Single](Function(t) t Is tree1)
Dim root = tree.GetRoot()
Dim model = compilation.GetSemanticModel(tree)
model.GetDiagnostics(root.FullSpan)
Assert.True(eventQueue.Count > 0)
Dim compilationStartedFired As Boolean
Dim declaredSymbolNames As HashSet(Of String) = Nothing, completedCompilationUnits As HashSet(Of String) = Nothing
Assert.True(DequeueCompilationEvents(eventQueue, compilationStartedFired, declaredSymbolNames, completedCompilationUnits))
' Verify symbol declared events fired for all symbols declared in the first source file.
Assert.True(compilationStartedFired)
Assert.True(declaredSymbolNames.Contains(compilation.GlobalNamespace.Name))
Assert.True(declaredSymbolNames.Contains("N1"))
Assert.True(declaredSymbolNames.Contains("C"))
Assert.True(declaredSymbolNames.Contains("NonPartialMethod1"))
Assert.True(completedCompilationUnits.Contains(tree.FilePath))
End Sub
<Fact, WorkItem(7477, "https://github.com/dotnet/roslyn/issues/7477")>
Public Sub TestCompilationEventsForPartialMethod()
Dim source1 = <file>
Namespace N1
Partial Class C
Private Sub NonPartialMethod1()
End Sub
Private Partial Sub PartialMethod() ' Declaration
End Sub
Private Partial Sub ImpartialMethod() ' Declaration
End Sub
End Class
End Namespace
</file>.Value
Dim source2 =
<file>
Namespace N1
Partial Class C
Private Sub NonPartialMethod2()
End Sub
Private Sub PartialMethod() ' Implementation
End Sub
End Class
End Namespace
</file>.Value
Dim tree1 = VisualBasicSyntaxTree.ParseText(source1, path:="file1")
Dim tree2 = VisualBasicSyntaxTree.ParseText(source2, path:="file2")
Dim eventQueue = New AsyncQueue(Of CompilationEvent)()
Dim compilation = CreateCompilationWithMscorlib461({tree1, tree2}).WithEventQueue(eventQueue)
' Invoke SemanticModel.GetDiagnostics to force populate the event queue for symbols in the first source file.
Dim tree = compilation.SyntaxTrees.[Single](Function(t) t Is tree1)
Dim root = tree.GetRoot()
Dim model = compilation.GetSemanticModel(tree)
model.GetDiagnostics(root.FullSpan)
Assert.True(eventQueue.Count > 0)
Dim compilationStartedFired As Boolean
Dim declaredSymbolNames As HashSet(Of String) = Nothing, completedCompilationUnits As HashSet(Of String) = Nothing
Assert.True(DequeueCompilationEvents(eventQueue, compilationStartedFired, declaredSymbolNames, completedCompilationUnits))
' Verify symbol declared events fired for all symbols declared in the first source file.
Assert.True(compilationStartedFired)
Assert.True(declaredSymbolNames.Contains(compilation.GlobalNamespace.Name))
Assert.True(declaredSymbolNames.Contains("N1"))
Assert.True(declaredSymbolNames.Contains("C"))
Assert.True(declaredSymbolNames.Contains("NonPartialMethod1"))
Assert.True(declaredSymbolNames.Contains("ImpartialMethod"))
Assert.True(declaredSymbolNames.Contains("PartialMethod"))
Assert.True(completedCompilationUnits.Contains(tree.FilePath))
End Sub
<Fact>
Public Sub CompilingCodeWithInvalidPreProcessorSymbolsShouldProvideDiagnostics()
Dim dict = New Dictionary(Of String, Object)
dict.Add("1", Nothing)
Dim compilation = CreateEmptyCompilation(String.Empty, parseOptions:=New VisualBasicParseOptions().WithPreprocessorSymbols(dict))
CompilationUtils.AssertTheseDiagnostics(compilation, <errors>
BC31030: Conditional compilation constant '1' is not valid: Identifier expected.
~
</errors>)
End Sub
<Fact>
Public Sub CompilingCodeWithInvalidSourceCodeKindShouldProvideDiagnostics()
#Disable Warning BC40000 ' Type or member is obsolete
Dim compilation = CreateCompilationWithMscorlib461(String.Empty, parseOptions:=New VisualBasicParseOptions().WithKind(SourceCodeKind.Interactive))
#Enable Warning BC40000 ' Type or member is obsolete
CompilationUtils.AssertTheseDiagnostics(compilation, <errors>
BC37285: Provided source code kind is unsupported or invalid: 'Interactive'
~
</errors>)
End Sub
<Fact>
Public Sub CompilingCodeWithInvalidLanguageVersionShouldProvideDiagnostics()
Dim compilation = CreateEmptyCompilation(String.Empty, parseOptions:=New VisualBasicParseOptions().WithLanguageVersion(DirectCast(10000, LanguageVersion)))
CompilationUtils.AssertTheseDiagnostics(compilation, <errors>
BC37287: Provided language version is unsupported or invalid: '10000'.
~
</errors>)
End Sub
<Fact>
Public Sub CompilingCodeWithInvalidDocumentationModeShouldProvideDiagnostics()
Dim compilation = CreateEmptyCompilation(String.Empty, parseOptions:=New VisualBasicParseOptions().WithDocumentationMode(CType(100, DocumentationMode)))
CompilationUtils.AssertTheseDiagnostics(compilation, <errors>
BC37286: Provided documentation mode is unsupported or invalid: '100'.
~
</errors>)
End Sub
<Fact>
Public Sub CompilingCodeWithInvalidParseOptionsInMultipleSyntaxTreesShouldReportThemAll()
Dim dict1 = New Dictionary(Of String, Object)
dict1.Add("1", Nothing)
Dim dict2 = New Dictionary(Of String, Object)
dict2.Add("2", Nothing)
Dim dict3 = New Dictionary(Of String, Object)
dict3.Add("3", Nothing)
Dim syntaxTree1 = Parse(String.Empty, options:=New VisualBasicParseOptions().WithPreprocessorSymbols(dict1))
Dim syntaxTree2 = Parse(String.Empty, options:=New VisualBasicParseOptions().WithPreprocessorSymbols(dict2))
Dim syntaxTree3 = Parse(String.Empty, options:=New VisualBasicParseOptions().WithPreprocessorSymbols(dict3))
Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
Dim compilation = CreateCompilationWithMscorlib40({syntaxTree1, syntaxTree2, syntaxTree3}, options:=options)
Dim diagnostics = compilation.GetDiagnostics()
CompilationUtils.AssertTheseDiagnostics(diagnostics,
<errors>
BC31030: Conditional compilation constant '1' is not valid: Identifier expected.
~
BC31030: Conditional compilation constant '2' is not valid: Identifier expected.
~
BC31030: Conditional compilation constant '3' is not valid: Identifier expected.
~
</errors>)
Assert.True(diagnostics(0).Location.SourceTree.Equals(syntaxTree1))
Assert.True(diagnostics(1).Location.SourceTree.Equals(syntaxTree2))
Assert.True(diagnostics(2).Location.SourceTree.Equals(syntaxTree3))
End Sub
<Fact>
Public Sub CompilingCodeWithSameParseOptionsInMultipleSyntaxTreesShouldReportOnlyNonDuplicates()
Dim dict1 = New Dictionary(Of String, Object)
dict1.Add("1", Nothing)
Dim dict2 = New Dictionary(Of String, Object)
dict2.Add("2", Nothing)
Dim parseOptions1 = New VisualBasicParseOptions().WithPreprocessorSymbols(dict1)
Dim parseOptions2 = New VisualBasicParseOptions().WithPreprocessorSymbols(dict2)
Dim syntaxTree1 = Parse(String.Empty, options:=parseOptions1)
Dim syntaxTree2 = Parse(String.Empty, options:=parseOptions2)
Dim syntaxTree3 = Parse(String.Empty, options:=parseOptions2)
Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
Dim compilation = CreateCompilationWithMscorlib40({syntaxTree1, syntaxTree2, syntaxTree3}, options:=options)
Dim diagnostics = compilation.GetDiagnostics()
CompilationUtils.AssertTheseDiagnostics(diagnostics,
<errors>
BC31030: Conditional compilation constant '1' is not valid: Identifier expected.
~
BC31030: Conditional compilation constant '2' is not valid: Identifier expected.
~
</errors>)
Assert.True(diagnostics(0).Location.SourceTree.Equals(syntaxTree1))
Assert.True(diagnostics(1).Location.SourceTree.Equals(syntaxTree2))
End Sub
<Fact>
Public Sub DiagnosticsInCompilationOptionsParseOptionsAreDedupedWithParseTreesParseOptions()
Dim dict1 = New Dictionary(Of String, Object)
dict1.Add("1", Nothing)
Dim dict2 = New Dictionary(Of String, Object)
dict2.Add("2", Nothing)
Dim parseOptions1 = New VisualBasicParseOptions().WithPreprocessorSymbols(dict1)
Dim parseOptions2 = New VisualBasicParseOptions().WithPreprocessorSymbols(dict2)
Dim syntaxTree1 = Parse(String.Empty, options:=parseOptions1)
Dim syntaxTree2 = Parse(String.Empty, options:=parseOptions2)
Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, parseOptions:=parseOptions1)
Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime({syntaxTree1, syntaxTree2}, options:=options)
Dim diagnostics = compilation.GetDiagnostics()
CompilationUtils.AssertTheseDiagnostics(diagnostics,
<errors>
BC31030: Conditional compilation constant '1' is not valid: Identifier expected.
BC31030: Conditional compilation constant '2' is not valid: Identifier expected.
~
</errors>)
Assert.Equal("2", diagnostics(0).Arguments(1))
Assert.True(diagnostics(0).Location.SourceTree.Equals(syntaxTree2)) ' Syntax tree parse options are reported in CompilationStage.Parse
Assert.Equal("1", diagnostics(1).Arguments(1))
Assert.Null(diagnostics(1).Location.SourceTree) ' Compilation parse options are reported in CompilationStage.Declare
End Sub
<Fact>
Public Sub DiagnosticsInCompilationOptionsParseOptionsAreReportedSeparately()
Dim dict1 = New Dictionary(Of String, Object)
dict1.Add("1", Nothing)
Dim dict2 = New Dictionary(Of String, Object)
dict2.Add("2", Nothing)
Dim parseOptions1 = New VisualBasicParseOptions().WithPreprocessorSymbols(dict1)
Dim parseOptions2 = New VisualBasicParseOptions().WithPreprocessorSymbols(dict2)
Dim options = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, parseOptions:=parseOptions1)
CompilationUtils.AssertTheseDiagnostics(options.Errors,
<errors>
BC31030: Conditional compilation constant '1' is not valid: Identifier expected.
</errors>)
Dim syntaxTree = Parse(String.Empty, options:=parseOptions2)
Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime({syntaxTree}, options:=options)
Dim diagnostics = compilation.GetDiagnostics()
CompilationUtils.AssertTheseDiagnostics(diagnostics,
<errors>
BC31030: Conditional compilation constant '1' is not valid: Identifier expected.
BC31030: Conditional compilation constant '2' is not valid: Identifier expected.
~
</errors>)
Assert.Equal("2", diagnostics(0).Arguments(1))
Assert.True(diagnostics(0).Location.SourceTree.Equals(syntaxTree)) ' Syntax tree parse options are reported in CompilationStage.Parse
Assert.Equal("1", diagnostics(1).Arguments(1))
Assert.Null(diagnostics(1).Location.SourceTree) ' Compilation parse options are reported in CompilationStage.Declare
End Sub
Private Shared Function DequeueCompilationEvents(eventQueue As AsyncQueue(Of CompilationEvent), ByRef compilationStartedFired As Boolean, ByRef declaredSymbolNames As HashSet(Of String), ByRef completedCompilationUnits As HashSet(Of String)) As Boolean
compilationStartedFired = False
declaredSymbolNames = New HashSet(Of String)()
completedCompilationUnits = New HashSet(Of String)()
If eventQueue.Count = 0 Then
Return False
End If
Dim compEvent As CompilationEvent = Nothing
While eventQueue.TryDequeue(compEvent)
If TypeOf compEvent Is CompilationStartedEvent Then
Assert.[False](compilationStartedFired, "Unexpected multiple compilation stated events")
compilationStartedFired = True
Else
Dim symbolDeclaredEvent = TryCast(compEvent, SymbolDeclaredCompilationEvent)
If symbolDeclaredEvent IsNot Nothing Then
Dim symbol = symbolDeclaredEvent.Symbol
Dim added = declaredSymbolNames.Add(symbol.Name)
If Not added Then
Dim method = TryCast(symbol, Symbols.MethodSymbol)
Assert.NotNull(method)
Dim isPartialMethod = method.PartialDefinitionPart IsNot Nothing OrElse
method.PartialImplementationPart IsNot Nothing
Assert.True(isPartialMethod, "Unexpected multiple symbol declared events for same symbol " + symbol.Name)
End If
Else
Dim compilationCompletedEvent = TryCast(compEvent, CompilationUnitCompletedEvent)
If compilationCompletedEvent IsNot Nothing Then
Assert.True(completedCompilationUnits.Add(compilationCompletedEvent.CompilationUnit.FilePath))
End If
End If
End If
End While
Return True
End Function
<Fact>
Public Sub TestEventQueueCompletionForEmptyCompilation()
Dim compilation = CreateCompilationWithMscorlib461(source:=Nothing).WithEventQueue(New AsyncQueue(Of CompilationEvent)())
' Force complete compilation event queue
Dim unused = compilation.GetDiagnostics()
Assert.True(compilation.EventQueue.IsCompleted)
End Sub
<Theory, CombinatorialData, WorkItem(67310, "https://github.com/dotnet/roslyn/issues/67310")>
Public Async Function TestBlockStartAnalyzer(testCodeBlockStart As Boolean) As Task
Dim source = "
Imports System
Class D
Private _field As Integer
Private Property P As Integer
Get
Return 0
End Get
Set(value As Integer)
value = 0
End Set
End Property
Private Property Item(i As Char) As Integer
Get
Return 0
End Get
Set(value As Integer)
value = 0
End Set
End Property
Public Custom Event E As EventHandler
AddHandler(value As EventHandler)
Dim x = 0
End AddHandler
RemoveHandler(value As EventHandler)
Dim x = 0
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
Dim x = 0
End RaiseEvent
End Event
Private Function M() As Integer
Return 0
End Function
Private Sub New()
_field = 0
End Sub
Protected Overrides Sub Finalize()
_field = 0
End Sub
Public Shared Operator +(value As D) As Integer
Return 0
End Operator
End Class"
Dim compilation = CreateCompilation(source)
Dim syntaxTree = compilation.SyntaxTrees(0)
Dim analyzer = New BlockStartAnalyzer(testCodeBlockStart)
Dim compilationWithAnalyzers = compilation.WithAnalyzers(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer), AnalyzerOptions.Empty)
Dim result = Await compilationWithAnalyzers.GetAnalysisResultAsync(CancellationToken.None)
Dim semanticDiagnostics = result.SemanticDiagnostics(syntaxTree)(analyzer)
Dim group1 = semanticDiagnostics.Where(Function(d) d.Id = "ID0001")
Dim group2 = semanticDiagnostics.Except(group1).ToImmutableArray()
group1.Verify(
Diagnostic("ID0001", "Private Function M() As Integer").WithArguments("M").WithLocation(41, 5),
Diagnostic("ID0001", "Private Sub New()").WithArguments(".ctor").WithLocation(45, 5),
Diagnostic("ID0001", "Protected Overrides Sub Finalize()").WithArguments("Finalize").WithLocation(49, 5),
Diagnostic("ID0001", "Public Shared Operator +(value As D) As Integer").WithArguments("op_UnaryPlus").WithLocation(53, 5))
group2.Verify(
Diagnostic("ID0002", "Get
Return 0
End Get").WithLocation(8, 9),
Diagnostic("ID0002", "Set(value As Integer)
value = 0
End Set").WithLocation(12, 9),
Diagnostic("ID0002", "Get
Return 0
End Get").WithLocation(18, 9),
Diagnostic("ID0002", "Set(value As Integer)
value = 0
End Set").WithLocation(22, 9),
Diagnostic("ID0002", "AddHandler(value As EventHandler)
Dim x = 0
End AddHandler").WithLocation(28, 9),
Diagnostic("ID0002", "RemoveHandler(value As EventHandler)
Dim x = 0
End RemoveHandler").WithLocation(32, 9),
Diagnostic("ID0002", "RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
Dim x = 0
End RaiseEvent").WithLocation(36, 9),
Diagnostic("ID0002", "Private Function M() As Integer
Return 0
End Function").WithLocation(41, 5),
Diagnostic("ID0002", "Private Sub New()
_field = 0
End Sub").WithLocation(45, 5),
Diagnostic("ID0002", "Protected Overrides Sub Finalize()
_field = 0
End Sub").WithLocation(49, 5),
Diagnostic("ID0002", "Public Shared Operator +(value As D) As Integer
Return 0
End Operator").WithLocation(53, 5))
result.CompilationDiagnostics(analyzer).Verify(
Diagnostic("ID0001", "Private Property P As Integer").WithArguments("set_P").WithLocation(7, 5),
Diagnostic("ID0001", "Private Property P As Integer").WithArguments("get_P").WithLocation(7, 5),
Diagnostic("ID0001", "Private Property Item(i As Char) As Integer").WithArguments("set_Item").WithLocation(17, 5),
Diagnostic("ID0001", "Private Property Item(i As Char) As Integer").WithArguments("get_Item").WithLocation(17, 5),
Diagnostic("ID0001", "Public Custom Event E As EventHandler").WithArguments("add_E").WithLocation(27, 5),
Diagnostic("ID0001", "Public Custom Event E As EventHandler").WithArguments("raise_E").WithLocation(27, 5),
Diagnostic("ID0001", "Public Custom Event E As EventHandler").WithArguments("remove_E").WithLocation(27, 5))
Assert.Empty(result.SyntaxDiagnostics)
End Function
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Private Class BlockStartAnalyzer
Inherits DiagnosticAnalyzer
Public Shared ReadOnly Descriptor As DiagnosticDescriptor = New DiagnosticDescriptor("ID0001", "Title", "{0}", "Category", defaultSeverity:=DiagnosticSeverity.Warning, isEnabledByDefault:=True)
Public Shared ReadOnly DescriptorForBlockEnd As DiagnosticDescriptor = New DiagnosticDescriptor("ID0002", "Title", "Message", "Category", defaultSeverity:=DiagnosticSeverity.Warning, isEnabledByDefault:=True)
Private ReadOnly _testCodeBlockStart As Boolean
Public Sub New(ByVal testCodeBlockStart As Boolean)
_testCodeBlockStart = testCodeBlockStart
End Sub
Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor)
Get
Return ImmutableArray.Create(Descriptor, DescriptorForBlockEnd)
End Get
End Property
Public Overrides Sub Initialize(ByVal context As AnalysisContext)
context.RegisterCompilationStartAction(AddressOf OnCompilationStart)
End Sub
Private Sub OnCompilationStart(context As CompilationStartAnalysisContext)
If (_testCodeBlockStart) Then
context.RegisterCodeBlockStartAction(Sub(blockStartContext As CodeBlockStartAnalysisContext(Of SyntaxKind))
blockStartContext.RegisterSyntaxNodeAction(AddressOf AnalyzeNumericalLiteralExpressionNode, SyntaxKind.NumericLiteralExpression)
blockStartContext.RegisterCodeBlockEndAction(AddressOf AnalyzeCodeBlockEnd)
End Sub)
Else
context.RegisterOperationBlockStartAction(Sub(blockStartContext As OperationBlockStartAnalysisContext)
blockStartContext.RegisterOperationAction(AddressOf AnalyzeOperationContext, OperationKind.Literal)
blockStartContext.RegisterOperationBlockEndAction(AddressOf AnalyzeOperationBlockEnd)
End Sub)
End If
Dim uniqueCallbacks As New HashSet(Of SyntaxNode)
context.RegisterSyntaxNodeAction(Sub(nodeContext As SyntaxNodeAnalysisContext)
If Not uniqueCallbacks.Add(nodeContext.Node) Then
Throw New Exception($"Multiple callbacks for {nodeContext.Node}")
End If
End Sub,
SyntaxKind.PropertyBlock, SyntaxKind.EventBlock, SyntaxKind.FunctionBlock)
End Sub
Private Sub AnalyzeNumericalLiteralExpressionNode(context As SyntaxNodeAnalysisContext)
AnalyzeNode(context.Node, context.ContainingSymbol, AddressOf context.ReportDiagnostic)
End Sub
Private Sub AnalyzeCodeBlockEnd(context As CodeBlockAnalysisContext)
context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(DescriptorForBlockEnd, context.CodeBlock.GetLocation()))
If TryCast(context.CodeBlock, PropertyBlockSyntax) IsNot Nothing OrElse
TryCast(context.CodeBlock, EventBlockSyntax) IsNot Nothing Then
Throw New Exception($"Unexpected topmost node for code block '{context.CodeBlock.Kind()}'")
End If
End Sub
Private Sub AnalyzeOperationContext(context As OperationAnalysisContext)
AnalyzeNode(context.Operation.Syntax, context.ContainingSymbol, AddressOf context.ReportDiagnostic)
End Sub
Private Sub AnalyzeOperationBlockEnd(context As OperationBlockAnalysisContext)
For Each operationBlock In context.OperationBlocks
context.ReportDiagnostic(CodeAnalysis.Diagnostic.Create(DescriptorForBlockEnd, operationBlock.Syntax.GetLocation()))
If TryCast(operationBlock.Syntax, PropertyBlockSyntax) IsNot Nothing OrElse
TryCast(operationBlock.Syntax, EventBlockSyntax) IsNot Nothing Then
Throw New Exception($"Unexpected topmost node for operation block '{operationBlock.Syntax.Kind()}'")
End If
Next
End Sub
Private Sub AnalyzeNode(node As SyntaxNode, containingSymbol As ISymbol, reportDiagnostic As Action(Of Diagnostic))
Dim location As Location
Dim propertyBlock = node.FirstAncestorOrSelf(Of PropertyBlockSyntax)
If propertyBlock IsNot Nothing Then
location = propertyBlock.PropertyStatement.GetLocation()
Else
Dim eventBlock = node.FirstAncestorOrSelf(Of EventBlockSyntax)
If eventBlock IsNot Nothing Then
location = eventBlock.EventStatement.GetLocation()
Else
Dim methodBlock = node.FirstAncestorOrSelf(Of MethodBlockBaseSyntax)
If methodBlock IsNot Nothing Then
location = methodBlock.BlockStatement.GetLocation()
Else
Return
End If
End If
End If
reportDiagnostic(CodeAnalysis.Diagnostic.Create(Descriptor, location, containingSymbol.Name))
End Sub
End Class
<Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68654")>
Public Async Function TestAnalyzerLocalDiagnosticsWhenReportedOnEnumFieldSymbol() As Task
Dim source = "
Public Class Outer
Public Enum E1
A1 = 0
End Enum
End Class
Public Enum E2
A2 = 0
End Enum"
Dim compilation = CreateCompilation(source)
compilation.VerifyDiagnostics()
Dim tree = compilation.SyntaxTrees(0)
Dim analyzer = New EnumTypeFieldSymbolAnalyzer()
Dim compilationWithAnalyzers = compilation.WithAnalyzers(ImmutableArray.Create(Of DiagnosticAnalyzer)(analyzer), AnalyzerOptions.Empty)
Dim result = Await compilationWithAnalyzers.GetAnalysisResultAsync(CancellationToken.None)
Dim localSemanticDiagnostics = result.SemanticDiagnostics(tree)(analyzer)
localSemanticDiagnostics.Verify(
Diagnostic("ID0001", "A1 = 0").WithLocation(4, 9),
Diagnostic("ID0001", "A2 = 0").WithLocation(9, 5))
Assert.Empty(result.CompilationDiagnostics)
End Function
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Private Class EnumTypeFieldSymbolAnalyzer
Inherits DiagnosticAnalyzer
Public Shared ReadOnly Descriptor As New DiagnosticDescriptor("ID0001", "Title", "Message", "Category", defaultSeverity:=DiagnosticSeverity.Warning, isEnabledByDefault:=True)
Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor)
Get
Return ImmutableArray.Create(Descriptor)
End Get
End Property
Public Overrides Sub Initialize(context As AnalysisContext)
context.RegisterSymbolAction(Sub(symbolContext As SymbolAnalysisContext)
Dim namedType = DirectCast(symbolContext.Symbol, INamedTypeSymbol)
For Each field In namedType.GetMembers().OfType(Of IFieldSymbol)
If Not field.IsImplicitlyDeclared Then
Dim diag = CodeAnalysis.Diagnostic.Create(Descriptor, field.DeclaringSyntaxReferences(0).GetLocation())
symbolContext.ReportDiagnostic(diag)
End If
Next
End Sub,
SymbolKind.NamedType)
End Sub
End Class
End Class
End Namespace
|