File: DiagnosticAnalyzer\DiagnosticAnalysisContextHelpers.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// 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;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.CodeAnalysis.FlowAnalysis;
using Microsoft.CodeAnalysis.Operations;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Diagnostics
{
    internal static class DiagnosticAnalysisContextHelpers
    {
        internal static void VerifyArguments<TContext>(Action<TContext> action)
        {
            VerifyAction(action);
        }
 
        internal static void VerifyArguments<TContext>(Action<TContext> action, ImmutableArray<SymbolKind> symbolKinds)
        {
            VerifyAction(action);
            VerifySymbolKinds(symbolKinds);
        }
 
        internal static void VerifyArguments<TContext, TLanguageKindEnum>(Action<TContext> action, ImmutableArray<TLanguageKindEnum> syntaxKinds)
            where TLanguageKindEnum : struct
        {
            VerifyAction(action);
            VerifySyntaxKinds(syntaxKinds);
        }
 
        internal static void VerifyArguments<TContext>(Action<TContext> action, ImmutableArray<OperationKind> operationKinds)
        {
            VerifyAction(action);
            VerifyOperationKinds(operationKinds);
        }
 
        internal static void VerifyArguments(Diagnostic diagnostic, Compilation? compilation, Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic, CancellationToken cancellationToken)
        {
            if (diagnostic is DiagnosticWithInfo)
            {
                // Compiler diagnostic, skip validations.
                return;
            }
 
            if (diagnostic == null)
            {
                throw new ArgumentNullException(nameof(diagnostic));
            }
 
            if (compilation != null)
            {
                VerifyDiagnosticLocationsInCompilation(diagnostic, compilation);
            }
 
            if (!isSupportedDiagnostic(diagnostic, cancellationToken))
            {
                throw new ArgumentException(string.Format(CodeAnalysisResources.UnsupportedDiagnosticReported, diagnostic.Id), nameof(diagnostic));
            }
 
            if (!UnicodeCharacterUtilities.IsValidIdentifier(diagnostic.Id))
            {
                // Disallow invalid diagnostic IDs.
                // Note that the parsing logic in Csc/Vbc MSBuild tasks to decode command line compiler output relies on diagnostics having a valid ID.
                // See https://github.com/dotnet/roslyn/issues/4376 for details.
                throw new ArgumentException(string.Format(CodeAnalysisResources.InvalidDiagnosticIdReported, diagnostic.Id), nameof(diagnostic));
            }
        }
 
        internal static void VerifyDiagnosticLocationsInCompilation(Diagnostic diagnostic, Compilation compilation)
        {
            VerifyDiagnosticLocationInCompilation(diagnostic.Id, diagnostic.Location, compilation);
 
            if (diagnostic.AdditionalLocations != null)
            {
                foreach (var location in diagnostic.AdditionalLocations)
                {
                    VerifyDiagnosticLocationInCompilation(diagnostic.Id, location, compilation);
                }
            }
        }
 
        private static void VerifyDiagnosticLocationInCompilation(string id, Location location, Compilation compilation)
        {
            if (!location.IsInSource)
            {
                return;
            }
 
            Debug.Assert(location.SourceTree != null);
            if (!compilation.ContainsSyntaxTree(location.SourceTree))
            {
                // Disallow diagnostics with source locations outside this compilation.
                throw new ArgumentException(string.Format(CodeAnalysisResources.InvalidDiagnosticLocationReported, id, location.SourceTree.FilePath), "diagnostic");
            }
 
            if (location.SourceSpan.End > location.SourceTree.Length)
            {
                // Disallow diagnostics with source locations outside this compilation.
                throw new ArgumentException(string.Format(CodeAnalysisResources.InvalidDiagnosticSpanReported, id, location.SourceSpan, location.SourceTree.FilePath), "diagnostic");
            }
        }
 
        private static void VerifyAction<TContext>(Action<TContext> action)
        {
            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }
        }
 
        private static void VerifySymbolKinds(ImmutableArray<SymbolKind> symbolKinds)
        {
            if (symbolKinds.IsDefault)
            {
                throw new ArgumentNullException(nameof(symbolKinds));
            }
 
            if (symbolKinds.IsEmpty)
            {
                throw new ArgumentException(CodeAnalysisResources.ArgumentCannotBeEmpty, nameof(symbolKinds));
            }
        }
 
        private static void VerifySyntaxKinds<TLanguageKindEnum>(ImmutableArray<TLanguageKindEnum> syntaxKinds)
            where TLanguageKindEnum : struct
        {
            if (syntaxKinds.IsDefault)
            {
                throw new ArgumentNullException(nameof(syntaxKinds));
            }
 
            if (syntaxKinds.IsEmpty)
            {
                throw new ArgumentException(CodeAnalysisResources.ArgumentCannotBeEmpty, nameof(syntaxKinds));
            }
        }
 
        private static void VerifyOperationKinds(ImmutableArray<OperationKind> operationKinds)
        {
            if (operationKinds.IsDefault)
            {
                throw new ArgumentNullException(nameof(operationKinds));
            }
 
            if (operationKinds.IsEmpty)
            {
                throw new ArgumentException(CodeAnalysisResources.ArgumentCannotBeEmpty, nameof(operationKinds));
            }
        }
 
        internal static void VerifyArguments<TKey, TValue>(TKey key, AnalysisValueProvider<TKey, TValue> valueProvider)
            where TKey : class
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
 
            if (valueProvider == null)
            {
                throw new ArgumentNullException(nameof(valueProvider));
            }
        }
 
        internal static ControlFlowGraph GetControlFlowGraph(IOperation operation, Func<IOperation, ControlFlowGraph>? getControlFlowGraph, CancellationToken cancellationToken)
        {
            IOperation rootOperation = operation.GetRootOperation();
            return getControlFlowGraph != null ?
                getControlFlowGraph(rootOperation) :
                ControlFlowGraph.CreateCore(rootOperation, nameof(rootOperation), cancellationToken);
        }
    }
}