|
' 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 System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.Diagnostics
Namespace Microsoft.CodeAnalysis.VisualBasic
''' <summary>
''' Applies Visual Basic-specific modification and filtering of <see cref="Diagnostic"/>s.
''' </summary>
Friend Class VisualBasicDiagnosticFilter
Private Shared ReadOnly s_alinkWarnings As ERRID() = {ERRID.WRN_ConflictingMachineAssembly,
ERRID.WRN_RefCultureMismatch,
ERRID.WRN_InvalidVersionFormat}
''' <summary>
''' Modifies an input <see cref="Diagnostic"/> per the given options. For example, the
''' severity may be escalated, or the <see cref="Diagnostic"/> may be filtered out entirely
''' (by returning null).
''' </summary>
''' <param name="diagnostic">The input diagnostic</param>
''' <param name="generalDiagnosticOption">How warning diagnostics should be reported</param>
''' <param name="specificDiagnosticOptions">How specific diagnostics should be reported</param>
''' <returns>A diagnostic updated to reflect the options, or null if it has been filtered out</returns>
Public Shared Function Filter(
diagnostic As Diagnostic,
generalDiagnosticOption As ReportDiagnostic,
specificDiagnosticOptions As IDictionary(Of String, ReportDiagnostic),
syntaxTreeOptions As SyntaxTreeOptionsProvider,
cancellationToken As CancellationToken) As Diagnostic
' Diagnostic ids must be processed in case-insensitive fashion in VB.
Dim caseInsensitiveSpecificDiagnosticOptions =
ImmutableDictionary.Create(Of String, ReportDiagnostic)(CaseInsensitiveComparison.Comparer).AddRange(specificDiagnosticOptions)
' Filter void diagnostics so that our callers don't have to perform resolution
' (which might copy the list of diagnostics).
If diagnostic.Severity = InternalDiagnosticSeverity.Void Then
Return Nothing
End If
' If diagnostic is not configurable, keep it as it is.
If diagnostic.IsNotConfigurable Then
If diagnostic.IsEnabledByDefault Then
' Enabled NotConfigurable should always be reported as it is.
Return diagnostic
Else
' Disabled NotConfigurable should never be reported.
Return Nothing
End If
End If
' In the native compiler, all warnings originating from alink.dll were issued
' under the id WRN_ALinkWarn - 1607. If nowarn:1607 is used they would get
' none of those warnings. In Roslyn, we've given each of these warnings their
' own number, so that they may be configured independently. To preserve compatibility
' if a user has specifically configured 1607 And we are reporting one of the alink warnings, use
' the configuration specified for 1607. As implemented, this could result in
' specifying warnaserror:1607 And getting a message saying "warning as error CS8012..."
' We don't permit configuring 1607 and independently configuring the new warnings.
Dim report As ReportDiagnostic
Dim hasSourceSuppression As Boolean = False
If (s_alinkWarnings.Contains(CType(diagnostic.Code, ERRID)) AndAlso
caseInsensitiveSpecificDiagnosticOptions.Keys.Contains(VisualBasic.MessageProvider.Instance.GetIdForErrorCode(ERRID.WRN_AssemblyGeneration1))) Then
report = GetDiagnosticReport(VisualBasic.MessageProvider.Instance.GetSeverity(ERRID.WRN_AssemblyGeneration1),
diagnostic.IsEnabledByDefault,
VisualBasic.MessageProvider.Instance.GetIdForErrorCode(ERRID.WRN_AssemblyGeneration1),
diagnostic.Location,
diagnostic.CustomTags,
generalDiagnosticOption,
caseInsensitiveSpecificDiagnosticOptions,
syntaxTreeOptions,
cancellationToken,
hasSourceSuppression)
Else
report = GetDiagnosticReport(
diagnostic.Severity,
diagnostic.IsEnabledByDefault,
diagnostic.Id,
diagnostic.Location,
diagnostic.CustomTags,
generalDiagnosticOption,
caseInsensitiveSpecificDiagnosticOptions,
syntaxTreeOptions,
cancellationToken,
hasSourceSuppression)
End If
If hasSourceSuppression Then
diagnostic = diagnostic.WithIsSuppressed(True)
End If
Return diagnostic.WithReportDiagnostic(report)
End Function
''' <summary>
''' Take a warning And return the final disposition of the given warning,
''' based on both command line options And pragmas. The diagnostic options
''' have precedence in the following order:
''' 1. Warning level
''' 2. Command line options (/nowarn, /warnaserror)
''' 3. Custom severity configuration applied by analyzer
''' 4. Editor config options (syntax tree level)
''' 5. Global analyzer config options (compilation level)
''' 6. Global warning level
'''
''' Global overrides are complicated. Global options never override suppression.
''' Even if you have generalDiagnosticOption = ReportDiagnostic.Error, a
''' suppressed diagnostic will not turn into an error. However, general diagnostic options
''' do override warnings and infos into suppressions, i.e. if you had a syntaxtree option
''' to turn a diagnostic into a warning, and the global option was Suppress, the diagnostic
''' would be suppressed.
'''
''' Pragmas are considered separately. If a diagnostic would not otherwise
''' be suppressed, but is suppressed by a pragma,
''' <paramref name="hasDisableDirectiveSuppression"/>
''' is true but the diagnostic is not reported as suppressed.
''' </summary>
Friend Shared Function GetDiagnosticReport(severity As DiagnosticSeverity,
isEnabledByDefault As Boolean,
id As String,
location As Location,
customTags As ImmutableArray(Of String),
generalDiagnosticOption As ReportDiagnostic,
caseInsensitiveSpecificDiagnosticOptions As IDictionary(Of String, ReportDiagnostic),
syntaxTreeOptions As SyntaxTreeOptionsProvider,
cancellationToken As CancellationToken,
<Out> ByRef hasDisableDirectiveSuppression As Boolean) As ReportDiagnostic
hasDisableDirectiveSuppression = False
Dim report As ReportDiagnostic
Dim tree = location?.SourceTree
Dim isSpecified As Boolean = False
Dim specifiedWarnAsErrorMinus As Boolean = False
' Global options depend on other options, so calculate those first
If caseInsensitiveSpecificDiagnosticOptions.TryGetValue(id, report) Then
' 2. Command line options (/nowarn, /warnaserror)
isSpecified = True
' 'ReportDiagnostic.Default' is added to SpecificDiagnosticOptions for "/warnaserror-:DiagnosticId",
If report = ReportDiagnostic.Default Then
specifiedWarnAsErrorMinus = True
End If
End If
Dim isCustomConfigured = False
If AnalyzerManager.HasCustomSeverityConfigurableTag(customTags) Then
' 3. Custom severity configuration applied by the analyzer.
' See https://github.com/dotnet/roslyn/issues/52991 for details.
isCustomConfigured = True
' Respect the custom analyzer configured severity, unless it was already configured with command line switch.
' However, if just "/warnaserror-:DiagnosticId" was specified on the command line, we do respect the custom configured severity.
If Not isSpecified OrElse specifiedWarnAsErrorMinus Then
isSpecified = True
report = DiagnosticDescriptor.MapSeverityToReport(severity)
' /warnaserror should promote warning to error.
If report = ReportDiagnostic.Warn AndAlso generalDiagnosticOption = ReportDiagnostic.Error AndAlso Not specifiedWarnAsErrorMinus Then
report = ReportDiagnostic.Error
End If
End If
End If
' Apply syntax tree options, if applicable.
If syntaxTreeOptions IsNot Nothing AndAlso
Not isCustomConfigured AndAlso
(Not isSpecified OrElse specifiedWarnAsErrorMinus) Then
' 4. Editor config options (syntax tree level)
' 5. Global analyzer config options (compilation level)
' Do not apply config options if it is bumping a warning to an error and "/warnaserror-:DiagnosticId" was specified on the command line.
Dim reportFromSyntaxTreeOptions As ReportDiagnostic
If ((tree IsNot Nothing AndAlso syntaxTreeOptions.TryGetDiagnosticValue(tree, id, cancellationToken, reportFromSyntaxTreeOptions)) OrElse
syntaxTreeOptions.TryGetGlobalDiagnosticValue(id, cancellationToken, reportFromSyntaxTreeOptions)) AndAlso
Not (specifiedWarnAsErrorMinus AndAlso severity = DiagnosticSeverity.Warning AndAlso reportFromSyntaxTreeOptions = ReportDiagnostic.Error) Then
isSpecified = True
report = reportFromSyntaxTreeOptions
' '/warnaserror' should promote warnings configured in analyzer config to error.
If Not specifiedWarnAsErrorMinus AndAlso report = ReportDiagnostic.Warn AndAlso generalDiagnosticOption = ReportDiagnostic.Error Then
report = ReportDiagnostic.Error
End If
End If
End If
If Not isSpecified Then
report = If(isEnabledByDefault, ReportDiagnostic.Default, ReportDiagnostic.Suppress)
End If
' Compute if the reporting should be suppressed.
If report = ReportDiagnostic.Suppress Then
Return ReportDiagnostic.Suppress
End If
' If location is available, check warning directive state.
If location IsNot Nothing AndAlso location.SourceTree IsNot Nothing AndAlso
location.SourceTree.GetWarningState(id, location.SourceSpan.Start) = ReportDiagnostic.Suppress Then
hasDisableDirectiveSuppression = True
End If
' 1. Global options
' If present, they override previous options
' check options (/nowarn)
' When doing suppress-all-warnings, don't lower severity for anything other than warning and info.
' We shouldn't suppress hidden diagnostics here because then features that use hidden diagnostics to
' display light bulb would stop working if someone has suppress-all-warnings (/nowarn) specified in their project.
If generalDiagnosticOption = ReportDiagnostic.Suppress AndAlso
(severity = DiagnosticSeverity.Warning OrElse severity = DiagnosticSeverity.Info) Then
Return ReportDiagnostic.Suppress
End If
' check the AllWarningsAsErrors flag and the specific lists from /warnaserror[+|-] option.
' If we've been asked to do warn-as-error then don't raise severity for anything below warning (info or hidden).
If (generalDiagnosticOption = ReportDiagnostic.Error) AndAlso (severity = DiagnosticSeverity.Warning) Then
' In the case for both /warnaserror and /warnaserror-:<n> at the same time,
' do not report it as an error.
' If there has been no specific action for this warning, then turn it into an error.
If (Not isSpecified) AndAlso (report = ReportDiagnostic.Default) Then
Return ReportDiagnostic.Error
End If
End If
Return report
End Function
End Class
End Namespace
|