|
// 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.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Syntax;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Syntax
{
/// <summary>
/// Describes how to report a warning diagnostic.
/// </summary>
internal enum PragmaWarningState : byte
{
/// <summary>
/// Report a diagnostic by default.
/// Either there is no corresponding #pragma, or the action is "restore".
/// </summary>
Default = 0,
/// <summary>
/// Diagnostic is enabled.
/// NOTE: this may be removed as part of https://github.com/dotnet/roslyn/issues/36550
/// </summary>
Enabled = 1,
/// <summary>
/// Diagnostic is disabled.
/// </summary>
Disabled = 2,
}
internal class CSharpPragmaWarningStateMap : AbstractWarningStateMap<PragmaWarningState>
{
public CSharpPragmaWarningStateMap(SyntaxTree syntaxTree) :
base(syntaxTree)
{
}
protected override WarningStateMapEntry[] CreateWarningStateMapEntries(SyntaxTree syntaxTree)
{
// Accumulate all the pragma warning directives, in source code order
var directives = ArrayBuilder<DirectiveTriviaSyntax>.GetInstance();
GetAllPragmaWarningDirectives(syntaxTree, directives);
// Create the pragma warning map.
WarningStateMapEntry[] result = CreatePragmaWarningStateEntries(directives);
directives.Free();
return result;
}
// Add all active #pragma warn and #nullable directives under trivia into the list, in source code order.
private static void GetAllPragmaWarningDirectives(SyntaxTree syntaxTree, ArrayBuilder<DirectiveTriviaSyntax> directiveList)
{
foreach (var d in syntaxTree.GetRoot().GetDirectives())
{
if (!d.IsActive || d.Kind() != SyntaxKind.PragmaWarningDirectiveTrivia)
{
continue;
}
var w = (PragmaWarningDirectiveTriviaSyntax)d;
// Ignore directives with errors (i.e., Unrecognized #pragma directive)
if (!w.DisableOrRestoreKeyword.IsMissing && !w.WarningKeyword.IsMissing)
{
directiveList.Add(w);
}
}
}
// Given the ordered list of all pragma warning and nullable directives in the syntax tree, return a list of mapping entries,
// containing the cumulative set of warnings that are disabled for that point in the source.
// This mapping also contains a global warning option, accumulated of all #pragma up to the current line position.
private static WarningStateMapEntry[] CreatePragmaWarningStateEntries(ArrayBuilder<DirectiveTriviaSyntax> directiveList)
{
var entries = new WarningStateMapEntry[directiveList.Count + 1];
var index = 0;
// Captures the mapping of a warning number to the reporting option, accumulated of all #pragma up to the current directive.
var accumulatedSpecificWarningState = ImmutableDictionary.Create<string, PragmaWarningState>();
// Captures the general reporting option, accumulated of all #pragma up to the current directive.
var accumulatedGeneralWarningState = PragmaWarningState.Default;
var current = new WarningStateMapEntry(0, PragmaWarningState.Default, accumulatedSpecificWarningState);
entries[index] = current;
while (index < directiveList.Count)
{
var currentDirective = directiveList[index];
var currentPragmaDirective = (PragmaWarningDirectiveTriviaSyntax)currentDirective;
// Compute the directive state
PragmaWarningState directiveState = currentPragmaDirective.DisableOrRestoreKeyword.Kind() switch
{
SyntaxKind.DisableKeyword => PragmaWarningState.Disabled,
SyntaxKind.RestoreKeyword => PragmaWarningState.Default,
SyntaxKind.EnableKeyword => PragmaWarningState.Enabled,
var kind => throw ExceptionUtilities.UnexpectedValue(kind)
};
// Check if this directive applies for all (e.g., #pragma warning disable)
if (currentPragmaDirective.ErrorCodes.Count == 0)
{
// Update the warning state and reset the specific one
accumulatedGeneralWarningState = directiveState;
accumulatedSpecificWarningState = ImmutableDictionary.Create<string, PragmaWarningState>();
}
else
{
// Compute warning numbers from the current directive's codes
for (int x = 0; x < currentPragmaDirective.ErrorCodes.Count; x++)
{
var currentErrorCode = currentPragmaDirective.ErrorCodes[x];
if (currentErrorCode.IsMissing || currentErrorCode.ContainsDiagnostics)
continue;
var errorId = string.Empty;
if (currentErrorCode.Kind() == SyntaxKind.NumericLiteralExpression)
{
var token = ((LiteralExpressionSyntax)currentErrorCode).Token;
errorId = MessageProvider.Instance.GetIdForErrorCode((int)token.Value!);
}
else if (currentErrorCode.Kind() == SyntaxKind.IdentifierName)
{
errorId = ((IdentifierNameSyntax)currentErrorCode).Identifier.ValueText;
}
if (!string.IsNullOrWhiteSpace(errorId))
{
// Update the state of this error code with the current directive state
accumulatedSpecificWarningState = accumulatedSpecificWarningState.SetItem(errorId, directiveState);
}
}
}
current = new WarningStateMapEntry(currentDirective.Location.SourceSpan.End, accumulatedGeneralWarningState, accumulatedSpecificWarningState);
++index;
entries[index] = current;
}
#if DEBUG
// Make sure the entries array is correctly sorted.
for (int i = 1; i < entries.Length - 1; ++i)
{
Debug.Assert(entries[i].CompareTo(entries[i + 1]) < 0);
}
#endif
return entries;
}
}
}
|