File: Options.cs
Web Access
Project: src\src\Tools\AnalyzerRunner\AnalyzerRunner.csproj (AnalyzerRunner)
// 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.
 
#nullable disable
 
using System;
using System.Collections.Immutable;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis.SolutionCrawler;
 
namespace AnalyzerRunner
{
    public sealed class Options
    {
        public readonly string AnalyzerPath;
        public readonly string SolutionPath;
        public readonly ImmutableHashSet<string> AnalyzerNames;
        public readonly ImmutableHashSet<string> RefactoringNodes;
 
        public readonly bool RunConcurrent;
        public readonly bool ReportSuppressedDiagnostics;
        public readonly bool ApplyChanges;
        public readonly bool UseAll;
        public readonly int Iterations;
 
        // Options specific to incremental analyzers
        public readonly ImmutableArray<string> IncrementalAnalyzerNames;
        public readonly bool FullSolutionAnalysis;
 
        // Options used by AnalyzerRunner CLI only
        internal readonly bool ShowStats;
        internal readonly bool ShowCompilerDiagnostics;
        internal readonly bool TestDocuments;
        internal readonly Func<string, bool> TestDocumentMatch;
        internal readonly int TestDocumentIterations;
        internal readonly string LogFileName;
        internal readonly string ProfileRoot;
 
        internal BackgroundAnalysisScope AnalysisScope
            => FullSolutionAnalysis ? BackgroundAnalysisScope.FullSolution : BackgroundAnalysisScope.OpenFiles;
 
        public Options(
            string analyzerPath,
            string solutionPath,
            ImmutableHashSet<string> analyzerIds,
            ImmutableHashSet<string> refactoringNodes,
            bool runConcurrent,
            bool reportSuppressedDiagnostics,
            bool applyChanges,
            bool useAll,
            int iterations,
            bool fullSolutionAnalysis,
            ImmutableArray<string> incrementalAnalyzerNames)
            : this(analyzerPath,
                  solutionPath,
                  analyzerIds,
                  refactoringNodes,
                  runConcurrent,
                  reportSuppressedDiagnostics,
                  applyChanges,
                  showStats: false,
                  showCompilerDiagnostics: false,
                  useAll,
                  iterations,
                  testDocuments: false,
                  testDocumentMatch: _ => false,
                  testDocumentIterations: 0,
                  logFileName: null,
                  profileRoot: null,
                  fullSolutionAnalysis,
                  incrementalAnalyzerNames)
        { }
 
        internal Options(
            string analyzerPath,
            string solutionPath,
            ImmutableHashSet<string> analyzerIds,
            ImmutableHashSet<string> refactoringNodes,
            bool runConcurrent,
            bool reportSuppressedDiagnostics,
            bool applyChanges,
            bool showStats,
            bool showCompilerDiagnostics,
            bool useAll,
            int iterations,
            bool testDocuments,
            Func<string, bool> testDocumentMatch,
            int testDocumentIterations,
            string logFileName,
            string profileRoot,
            bool fullSolutionAnalysis,
            ImmutableArray<string> incrementalAnalyzerNames)
        {
            AnalyzerPath = analyzerPath;
            SolutionPath = solutionPath;
            AnalyzerNames = analyzerIds;
            RefactoringNodes = refactoringNodes;
            RunConcurrent = runConcurrent;
            ReportSuppressedDiagnostics = reportSuppressedDiagnostics;
            ApplyChanges = applyChanges;
            ShowStats = showStats;
            ShowCompilerDiagnostics = showCompilerDiagnostics;
            UseAll = useAll;
            Iterations = iterations;
            TestDocuments = testDocuments;
            TestDocumentMatch = testDocumentMatch;
            TestDocumentIterations = testDocumentIterations;
            LogFileName = logFileName;
            ProfileRoot = profileRoot;
            FullSolutionAnalysis = fullSolutionAnalysis;
            IncrementalAnalyzerNames = incrementalAnalyzerNames;
        }
 
        internal static Options Create(string[] args)
        {
            string analyzerPath = null;
            string solutionPath = null;
            var builder = ImmutableHashSet.CreateBuilder<string>();
            var refactoringBuilder = ImmutableHashSet.CreateBuilder<string>();
            bool runConcurrent = false;
            bool reportSuppressedDiagnostics = false;
            bool applyChanges = false;
            bool showStats = false;
            bool showCompilerDiagnostics = false;
            bool useAll = false;
            int iterations = 1;
            bool testDocuments = false;
            Func<string, bool> testDocumentMatch = _ => true;
            int testDocumentIterations = 10;
            string logFileName = null;
            string profileRoot = null;
            var fullSolutionAnalysis = false;
            var incrementalAnalyzerNames = ImmutableArray.CreateBuilder<string>();
 
            int i = 0;
            while (i < args.Length)
            {
                var arg = args[i++];
                string ReadValue() => (i < args.Length) ? args[i++] : throw new InvalidDataException($"Missing value for option {arg}");
 
                switch (arg)
                {
                    case "/all":
                        useAll = true;
                        break;
                    case "/stats":
                        showStats = true;
                        break;
                    case "/compilerStats":
                        showCompilerDiagnostics = true;
                        break;
                    case "/concurrent":
                        runConcurrent = true;
                        break;
                    case "/editperf":
                        testDocuments = true;
                        break;
                    case var _ when arg.StartsWith("/editperf:"):
                        testDocuments = true;
                        var expression = new Regex(arg.Substring("/editperf:".Length), RegexOptions.Compiled | RegexOptions.IgnoreCase);
                        testDocumentMatch = documentPath => expression.IsMatch(documentPath);
                        break;
                    case var _ when arg.StartsWith("/edititer:"):
                        testDocumentIterations = int.Parse(arg.Substring("/edititer:".Length));
                        break;
                    case var _ when arg.StartsWith("/iter:"):
                        iterations = int.Parse(arg.Substring("/iter:".Length));
                        break;
                    case "/suppressed":
                        reportSuppressedDiagnostics = true;
                        break;
                    case "/apply":
                        applyChanges = true;
                        break;
                    case "/a":
                        builder.Add(ReadValue());
                        break;
                    case "/refactor":
                        refactoringBuilder.Add(ReadValue());
                        break;
                    case "/log":
                        logFileName = ReadValue();
                        break;
                    case "/profileroot":
                        profileRoot = ReadValue();
                        break;
                    case "/fsa":
                        fullSolutionAnalysis = true;
                        break;
                    case "/ia":
                        incrementalAnalyzerNames.Add(ReadValue());
                        break;
                    default:
                        if (analyzerPath == null)
                        {
                            analyzerPath = arg;
                        }
                        else if (solutionPath == null)
                        {
                            solutionPath = arg;
                        }
                        else
                        {
                            throw new InvalidDataException((arg.StartsWith("/", StringComparison.Ordinal)
                             ? "Unrecognized option " + arg
                             : "Unrecognized parameter " + arg));
                        }
 
                        break;
                }
            }
 
            if (analyzerPath == null)
            {
                throw new InvalidDataException("Missing analyzer path");
            }
 
            if (solutionPath == null)
            {
                throw new InvalidDataException("Missing solution path");
            }
 
            return new Options(
                analyzerPath: analyzerPath,
                solutionPath: solutionPath,
                analyzerIds: builder.ToImmutableHashSet(),
                refactoringNodes: refactoringBuilder.ToImmutableHashSet(),
                runConcurrent: runConcurrent,
                reportSuppressedDiagnostics: reportSuppressedDiagnostics,
                applyChanges: applyChanges,
                showStats: showStats,
                showCompilerDiagnostics: showCompilerDiagnostics,
                useAll: useAll,
                iterations: iterations,
                testDocuments: testDocuments,
                testDocumentMatch: testDocumentMatch,
                testDocumentIterations: testDocumentIterations,
                logFileName: logFileName,
                profileRoot: profileRoot,
                fullSolutionAnalysis: fullSolutionAnalysis,
                incrementalAnalyzerNames: incrementalAnalyzerNames.ToImmutable());
        }
    }
}