// 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 enable using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using Xunit; #if CODE_STYLE using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; using Microsoft.CodeAnalysis.Internal.Options; #else using Microsoft.CodeAnalysis.Options; #endif namespace Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions { internal static class CodeFixVerifierHelper { public static void VerifyStandardProperties(DiagnosticAnalyzer analyzer, bool verifyHelpLink = false) { VerifyMessageTitle(analyzer); VerifyMessageDescription(analyzer); if (verifyHelpLink) { VerifyMessageHelpLinkUri(analyzer); } } private static void VerifyMessageTitle(DiagnosticAnalyzer analyzer) { foreach (var descriptor in analyzer.SupportedDiagnostics) { if (descriptor.CustomTags.Contains(WellKnownDiagnosticTags.NotConfigurable)) { // The title only displayed for rule configuration continue; } Assert.NotEqual("", descriptor.Title?.ToString() ?? ""); } } private static void VerifyMessageDescription(DiagnosticAnalyzer analyzer) { foreach (var descriptor in analyzer.SupportedDiagnostics) { if (ShouldSkipMessageDescriptionVerification(descriptor)) { continue; } Assert.NotEqual("", descriptor.MessageFormat?.ToString() ?? ""); } return; // Local function static bool ShouldSkipMessageDescriptionVerification(DiagnosticDescriptor descriptor) { if (descriptor.CustomTags.Contains(WellKnownDiagnosticTags.NotConfigurable)) { if (!descriptor.IsEnabledByDefault || descriptor.DefaultSeverity == DiagnosticSeverity.Hidden) { // The message only displayed if either enabled and not hidden, or configurable return true; } } return false; } } private static void VerifyMessageHelpLinkUri(DiagnosticAnalyzer analyzer) { foreach (var descriptor in analyzer.SupportedDiagnostics) { Assert.NotEqual("", descriptor.HelpLinkUri ?? ""); } } public static (SourceText? analyzerConfig, IEnumerable> options) ConvertOptionsToAnalyzerConfig(string defaultFileExtension, OptionsCollection options) { if (options.Count == 0) { return (null, options); } var optionSet = new OptionSetWrapper(options.ToDictionary, OptionKey, object?>(option => option.Key, option => option.Value)); var remainingOptions = new List>(); var analyzerConfig = new StringBuilder(); analyzerConfig.AppendLine("root = true"); analyzerConfig.AppendLine(); analyzerConfig.AppendLine($"[*.{defaultFileExtension}]"); foreach (var (key, value) in options) { var editorConfigStorageLocation = key.Option.StorageLocations.OfType().FirstOrDefault(); if (editorConfigStorageLocation is null) { remainingOptions.Add(KeyValuePairUtil.Create(key, value)); continue; } analyzerConfig.AppendLine(editorConfigStorageLocation.GetEditorConfigString(value, optionSet)); } return (SourceText.From(analyzerConfig.ToString(), Encoding.UTF8), remainingOptions); } private sealed class OptionSetWrapper : OptionSet { private readonly Dictionary _options; public OptionSetWrapper(Dictionary options) { _options = options; } #if CODE_STYLE public override bool TryGetValue(string key, out string value) { throw new NotImplementedException(); } #else public override object? GetOption(OptionKey optionKey) { if (!_options.TryGetValue(optionKey, out var value)) { value = optionKey.Option.DefaultValue; } return value; } public override OptionSet WithChangedOption(OptionKey optionAndLanguage, object? value) => throw new NotSupportedException(); internal override IEnumerable GetChangedOptions(OptionSet optionSet) => SpecializedCollections.EmptyEnumerable(); #endif } } }