/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/VisualStudio/CSharp/Impl/ProjectSystemShim/CSharpProjectShim.OptionsProcessor.cs
265 строк
11 KB
Todd Grunke
Selectively persist the commandline to temporary storage (#78441)
13 май 2025, 05:29
Не верифицирован
13 май 2025, 05:29
4215d71
Код
Авторство
О чём код?
// 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.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop; using Microsoft.VisualStudio.LanguageServices.ProjectSystem.Legacy; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim; internal partial class CSharpProjectShim { private sealed class OptionsProcessor : AbstractLegacyProjectSystemProjectOptionsProcessor { private readonly ProjectSystemProject _projectSystemProject; private readonly object[] _options = new object[(int)CompilerOptions.LARGEST_OPTION_ID]; private string? _mainTypeName; private OutputKind _outputKind; public OptionsProcessor(ProjectSystemProject projectSystemProject, SolutionServices workspaceServices) : base(projectSystemProject, workspaceServices) { _projectSystemProject = projectSystemProject; } public object this[CompilerOptions compilerOption] { get { return _options[(int)compilerOption]; } set { if (object.Equals(_options[(int)compilerOption], value)) { return; } _options[(int)compilerOption] = value; UpdateProjectForNewHostValues(); } } protected override CompilationOptions ComputeCompilationOptionsWithHostValues(CompilationOptions compilationOptions, IRuleSetFile? ruleSetFile) { IDictionary<string, ReportDiagnostic>? ruleSetSpecificDiagnosticOptions; // Get options from the ruleset file, if any, first. That way project-specific // options can override them. ReportDiagnostic? ruleSetGeneralDiagnosticOption = null; // TODO: merge this core logic back down to the base of OptionsProcessor, since this should be the same for all languages. The CompilationOptions // would then already contain the right information, and could be updated accordingly by the language-specific logic. if (ruleSetFile != null) { ruleSetGeneralDiagnosticOption = ruleSetFile.GetGeneralDiagnosticOption(); ruleSetSpecificDiagnosticOptions = new Dictionary<string, ReportDiagnostic>(ruleSetFile.GetSpecificDiagnosticOptions()); } else { ruleSetSpecificDiagnosticOptions = new Dictionary<string, ReportDiagnostic>(); } ReportDiagnostic generalDiagnosticOption; var warningsAreErrors = GetNullableBooleanOption(CompilerOptions.OPTID_WARNINGSAREERRORS); if (warningsAreErrors.HasValue) { generalDiagnosticOption = warningsAreErrors.Value ? ReportDiagnostic.Error : ReportDiagnostic.Default; } else if (ruleSetGeneralDiagnosticOption.HasValue) { generalDiagnosticOption = ruleSetGeneralDiagnosticOption.Value; } else { generalDiagnosticOption = ReportDiagnostic.Default; } // Start with the rule set options var diagnosticOptions = new Dictionary<string, ReportDiagnostic>(ruleSetSpecificDiagnosticOptions); // Update the specific options based on the general settings if (warningsAreErrors.HasValue && warningsAreErrors.Value == true) { foreach (var pair in ruleSetSpecificDiagnosticOptions) { if (pair.Value == ReportDiagnostic.Warn) { diagnosticOptions[pair.Key] = ReportDiagnostic.Error; } } } // Update the specific options based on the specific settings foreach (var diagnosticID in ParseWarningCodes(CompilerOptions.OPTID_WARNASERRORLIST)) { diagnosticOptions[diagnosticID] = ReportDiagnostic.Error; } foreach (var diagnosticID in ParseWarningCodes(CompilerOptions.OPTID_WARNNOTASERRORLIST)) { if (ruleSetSpecificDiagnosticOptions.TryGetValue(diagnosticID, out var ruleSetOption)) { diagnosticOptions[diagnosticID] = ruleSetOption; } else { diagnosticOptions[diagnosticID] = ReportDiagnostic.Default; } } foreach (var diagnosticID in ParseWarningCodes(CompilerOptions.OPTID_NOWARNLIST)) { diagnosticOptions[diagnosticID] = ReportDiagnostic.Suppress; } if (!Enum.TryParse(GetStringOption(CompilerOptions.OPTID_PLATFORM, ""), ignoreCase: true, result: out Platform platform)) { platform = Platform.AnyCpu; } if (!int.TryParse(GetStringOption(CompilerOptions.OPTID_WARNINGLEVEL, defaultValue: ""), out var warningLevel)) { warningLevel = 4; } // TODO: appConfigPath: GetFilePathOption(CompilerOptions.OPTID_FUSIONCONFIG), bug #869604 return ((CSharpCompilationOptions)compilationOptions).WithAllowUnsafe(GetBooleanOption(CompilerOptions.OPTID_UNSAFE)) .WithOverflowChecks(GetBooleanOption(CompilerOptions.OPTID_CHECKED)) .WithCryptoKeyContainer(GetStringOption(CompilerOptions.OPTID_KEYNAME, defaultValue: null)) .WithCryptoKeyFile(GetFilePathRelativeOption(CompilerOptions.OPTID_KEYFILE)) .WithDelaySign(GetNullableBooleanOption(CompilerOptions.OPTID_DELAYSIGN)) .WithGeneralDiagnosticOption(generalDiagnosticOption) .WithMainTypeName(_mainTypeName) .WithModuleName(GetStringOption(CompilerOptions.OPTID_MODULEASSEMBLY, defaultValue: null)) .WithOptimizationLevel(GetBooleanOption(CompilerOptions.OPTID_OPTIMIZATIONS) ? OptimizationLevel.Release : OptimizationLevel.Debug) .WithOutputKind(_outputKind) .WithPlatform(platform) .WithSpecificDiagnosticOptions(diagnosticOptions) .WithWarningLevel(warningLevel); } private static string GetIdForErrorCode(int errorCode) => "CS" + errorCode.ToString("0000"); private IEnumerable<string> ParseWarningCodes(CompilerOptions compilerOptions) { Contract.ThrowIfFalse( compilerOptions is CompilerOptions.OPTID_NOWARNLIST or CompilerOptions.OPTID_WARNASERRORLIST or CompilerOptions.OPTID_WARNNOTASERRORLIST); foreach (var warning in GetStringOption(compilerOptions, defaultValue: "").Split([' ', ',', ';'], StringSplitOptions.RemoveEmptyEntries)) { var warningStringID = warning; if (int.TryParse(warning, out var warningId)) { warningStringID = GetIdForErrorCode(warningId); } yield return warningStringID; } } private bool? GetNullableBooleanOption(CompilerOptions optionID) => (bool?)_options[(int)optionID]; private bool GetBooleanOption(CompilerOptions optionID) => GetNullableBooleanOption(optionID).GetValueOrDefault(defaultValue: false); private string? GetFilePathRelativeOption(CompilerOptions optionID) { var path = GetStringOption(optionID, defaultValue: null); if (string.IsNullOrEmpty(path)) { return null; } var directory = Path.GetDirectoryName(_projectSystemProject.FilePath); if (!string.IsNullOrEmpty(directory)) { return FileUtilities.ResolveRelativePath(path, directory); } return null; } [return: NotNullIfNotNull(nameof(defaultValue))] private string? GetStringOption(CompilerOptions optionID, string? defaultValue) { var value = (string)_options[(int)optionID]; if (string.IsNullOrEmpty(value)) { return defaultValue; } else { return value; } } protected override ParseOptions ComputeParseOptionsWithHostValues(ParseOptions parseOptions) { var symbols = GetStringOption(CompilerOptions.OPTID_CCSYMBOLS, defaultValue: "").Split([';'], StringSplitOptions.RemoveEmptyEntries); // The base implementation of OptionsProcessor already tried this, but it didn't have the real documentation // path so we have to do it a second time var documentationMode = DocumentationMode.Parse; if (GetStringOption(CompilerOptions.OPTID_XML_DOCFILE, defaultValue: null) != null) { documentationMode = DocumentationMode.Diagnose; } LanguageVersionFacts.TryParse(GetStringOption(CompilerOptions.OPTID_COMPATIBILITY, defaultValue: ""), out var languageVersion); return ((CSharpParseOptions)parseOptions).WithKind(SourceCodeKind.Regular) .WithLanguageVersion(languageVersion) .WithPreprocessorSymbols(symbols.AsImmutable()) .WithDocumentationMode(documentationMode); } public void SetOutputFileType(OutputFileType fileType) { var newOutputKind = fileType switch { OutputFileType.Console => OutputKind.ConsoleApplication, OutputFileType.Windows => OutputKind.WindowsApplication, OutputFileType.Library => OutputKind.DynamicallyLinkedLibrary, OutputFileType.Module => OutputKind.NetModule, OutputFileType.AppContainer => OutputKind.WindowsRuntimeApplication, OutputFileType.WinMDObj => OutputKind.WindowsRuntimeMetadata, _ => throw new ArgumentException("fileType was not a valid OutputFileType", nameof(fileType)), }; if (_outputKind != newOutputKind) { _outputKind = newOutputKind; UpdateProjectForNewHostValues(); } } public void SetMainTypeName(string? mainTypeName) { if (_mainTypeName != mainTypeName) { _mainTypeName = mainTypeName; UpdateProjectForNewHostValues(); } } } }