Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

write out codestyle options to include their diagnostic ids #52916

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Diagnostics
{
Expand All @@ -28,6 +30,31 @@ public static bool TryGetMappedOptions(string diagnosticId, string language, out
(s_diagnosticIdToLanguageSpecificOptionsMap.TryGetValue(language, out var map) &&
map.TryGetValue(diagnosticId, out options));

public static ImmutableArray<string> GetDiagnosticIdsForOption(IOption2 option, string language)
jmarolf marked this conversation as resolved.
Show resolved Hide resolved
{
using var _1 = ArrayBuilder<string>.GetInstance(out var ids);
foreach (var (id, options) in s_diagnosticIdToOptionMap)
{
if (options.Any(o => o == option))
{
ids.Add(id);
}
}

if (s_diagnosticIdToLanguageSpecificOptionsMap.TryGetValue(language, out var map))
{
foreach (var (id, options) in map)
{
if (options.Any(o => o == option))
{
ids.Add(id);
}
}
}

return ids.OrderBy(StringComparer.OrdinalIgnoreCase).AsImmutable();
}

public static void AddOptionMapping(string diagnosticId, ImmutableHashSet<IPerLanguageOption> perLanguageOptions)
{
diagnosticId = diagnosticId ?? throw new ArgumentNullException(nameof(diagnosticId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.AddImports;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Formatting;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
Expand All @@ -17,25 +21,37 @@
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.UnitTests
{
[UseExportProvider]
public partial class SettingsUpdaterTests : TestBase
{
private static Workspace CreateWorkspaceWithProjectAndDocuments()
private static async Task<Workspace> CreateWorkspaceWithProjectAndDocumentsAsync()
jmarolf marked this conversation as resolved.
Show resolved Hide resolved
{
var projectId = ProjectId.CreateNewId();

var workspace = new AdhocWorkspace(EditorTestCompositions.EditorFeatures.GetHostServices(), WorkspaceKind.Host);

var analyzerType = typeof(CSharpUseExplicitTypeDiagnosticAnalyzer);
var analyzerReference = new AnalyzerFileReference(analyzerType.Assembly.Location, new TestAnalyzerAssemblyLoader());

Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution
.AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp)
.AddDocument(DocumentId.CreateNewId(projectId), "goo.cs", "public class Goo { }")
.AddAdditionalDocument(DocumentId.CreateNewId(projectId), "add.txt", "text")
.AddAnalyzerConfigDocument(DocumentId.CreateNewId(projectId), "editorcfg", SourceText.From(""), filePath: "/a/b/config")));

var project = workspace.CurrentSolution.Projects.First();
var runner = new InProcOrRemoteHostAnalyzerRunner(new DiagnosticAnalyzerInfoCache());
var compilationWithAnalyzers = (await project.GetCompilationAsync())?.WithAnalyzers(
analyzerReference.GetAnalyzers(project.Language).ToImmutableArray(),
new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution));

// no result for open file only analyzer unless forced
_ = await runner.AnalyzeProjectAsync(project, compilationWithAnalyzers!, forceExecuteAllAnalyzers: true, logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: CancellationToken.None);

return workspace;
}

Expand All @@ -53,7 +69,7 @@ private static AnalyzerConfigDocument CreateAnalyzerConfigDocument(Workspace wor

private static async Task TestAsync(string initialEditorConfig, string updatedEditorConfig, params (IOption2, object)[] options)
{
using var workspace = CreateWorkspaceWithProjectAndDocuments();
using var workspace = await CreateWorkspaceWithProjectAndDocumentsAsync();
var analyzerConfigDocument = CreateAnalyzerConfigDocument(workspace, initialEditorConfig);
var sourcetext = await analyzerConfigDocument.GetTextAsync(default);
var result = SettingsUpdateHelper.TryUpdateAnalyzerConfigDocument(sourcetext, analyzerConfigDocument.FilePath!, workspace.Options, options);
Expand All @@ -62,7 +78,7 @@ private static async Task TestAsync(string initialEditorConfig, string updatedEd

private static async Task TestAsync(string initialEditorConfig, string updatedEditorConfig, params (AnalyzerSetting, DiagnosticSeverity)[] options)
{
using var workspace = CreateWorkspaceWithProjectAndDocuments();
using var workspace = await CreateWorkspaceWithProjectAndDocumentsAsync();
var analyzerConfigDocument = CreateAnalyzerConfigDocument(workspace, initialEditorConfig);
var sourcetext = await analyzerConfigDocument.GetTextAsync(default);
var result = SettingsUpdateHelper.TryUpdateAnalyzerConfigDocument(sourcetext, analyzerConfigDocument.FilePath!, options);
Expand All @@ -86,10 +102,42 @@ public async Task TestAddNewBoolCodeStyleOptionWithSeverityAsync()
option.Notification = CodeStyle.NotificationOption2.Suggestion;
await TestAsync(
string.Empty,
"[*.cs]\r\ncsharp_style_throw_expression=true:suggestion",
@"[*.cs]
csharp_style_throw_expression=true
dotnet_diagnostic.IDE0016.severity=suggestion",
(CSharpCodeStyleOptions.PreferThrowExpression, option));
}

[Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)]
public async Task TestAddNewEnumCodeStyleOptionWithSeverityOldOptionFormatExistAsync()
{
var option = CSharpCodeStyleOptions.PreferredUsingDirectivePlacement.DefaultValue;
option.Value = AddImportPlacement.InsideNamespace;
option.Notification = CodeStyle.NotificationOption2.Warning;
await TestAsync(
@"[*.cs]
csharp_using_directive_placement=inside_namespace:silent",
@"[*.cs]
csharp_using_directive_placement=inside_namespace
dotnet_diagnostic.IDE0065.severity=warning",
(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement, option));
}

[Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)]
public async Task TestAddNewEnumCodeStyleOptionWithSeverityOldOptionFormatExistHeadingMatchesAllFileTypesAsync()
{
var option = CSharpCodeStyleOptions.PreferredUsingDirectivePlacement.DefaultValue;
option.Value = AddImportPlacement.InsideNamespace;
option.Notification = CodeStyle.NotificationOption2.Warning;
await TestAsync(
@"[*]
csharp_using_directive_placement=inside_namespace:silent",
@"[*]
csharp_using_directive_placement=inside_namespace
dotnet_diagnostic.IDE0065.severity=warning",
(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement, option));
}

[Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)]
public async Task TestAddNewEnumCodeStyleOptionWithSeverityAsync()
{
Expand All @@ -98,7 +146,7 @@ public async Task TestAddNewEnumCodeStyleOptionWithSeverityAsync()
option.Notification = CodeStyle.NotificationOption2.Warning;
await TestAsync(
string.Empty,
"[*.cs]\r\ncsharp_using_directive_placement=inside_namespace:warning",
"[*.cs]\r\ncsharp_using_directive_placement=inside_namespace\r\ndotnet_diagnostic.IDE0065.severity=warning",
(CSharpCodeStyleOptions.PreferredUsingDirectivePlacement, option));
}

Expand Down Expand Up @@ -235,7 +283,10 @@ public async Task TestAddMultimpleNewWhitespaceOptions()
{
await TestAsync(
string.Empty,
"[*.cs]\r\ncsharp_new_line_before_else=true\r\ncsharp_new_line_before_catch=true\r\ncsharp_new_line_before_finally=true",
@"[*.cs]
csharp_new_line_before_else=true
csharp_new_line_before_catch=true
csharp_new_line_before_finally=true",
(CSharpFormattingOptions2.NewLineForElse, true),
(CSharpFormattingOptions2.NewLineForCatch, true),
(CSharpFormattingOptions2.NewLineForFinally, true));
Expand Down Expand Up @@ -322,21 +373,22 @@ await TestAsync(
[Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)]
public async Task TestAnalyzerSettingsUpdaterService()
{
var workspace = CreateWorkspaceWithProjectAndDocuments();
var workspace = await CreateWorkspaceWithProjectAndDocumentsAsync();
var updater = new AnalyzerSettingsUpdater(workspace, "/a/b/config");
var id = "Test001";
var descriptor = new DiagnosticDescriptor(id: id, title: "", messageFormat: "", category: "Naming", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: false);
var analyzerSetting = new AnalyzerSetting(descriptor, ReportDiagnostic.Suppress, updater, Language.CSharp);
analyzerSetting.ChangeSeverity(DiagnosticSeverity.Error);
var updates = await updater.GetChangedEditorConfigAsync(default);
var update = Assert.Single(updates);
Assert.Equal($"[*.cs]\r\ndotnet_diagnostic.{id}.severity=error", update.NewText);
Assert.Equal($@"[*.cs]
dotnet_diagnostic.{id}.severity=error", update.NewText);
}

[Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)]
public async Task TestCodeStyleSettingUpdaterService()
{
var workspace = CreateWorkspaceWithProjectAndDocuments();
var workspace = await CreateWorkspaceWithProjectAndDocumentsAsync();
var updater = new OptionUpdater(workspace, "/a/b/config");
var setting = CodeStyleSetting.Create(CSharpCodeStyleOptions.AllowBlankLineAfterColonInConstructorInitializer,
"",
Expand All @@ -346,23 +398,79 @@ public async Task TestCodeStyleSettingUpdaterService()
setting.ChangeSeverity(DiagnosticSeverity.Error);
var updates = await updater.GetChangedEditorConfigAsync(default);
var update = Assert.Single(updates);
Assert.Equal("[*.cs]\r\ncsharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental=true:error", update.NewText);
var actual = update.NewText;
var expected = @"[*.cs]
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental=true
dotnet_diagnostic.IDE2004.severity=error";
Assert.Equal(expected, actual);
setting.ChangeValue(1);
updates = await updater.GetChangedEditorConfigAsync(default);
update = Assert.Single(updates);
Assert.Equal("[*.cs]\r\ncsharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental=false:error", update.NewText);
actual = update.NewText;
expected = @"[*.cs]
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental=false
dotnet_diagnostic.IDE2004.severity=error";
Assert.Equal(expected, actual);
}

[Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)]
public async Task TestFormattingSettingUpdaterService()
{
var workspace = CreateWorkspaceWithProjectAndDocuments();
var workspace = await CreateWorkspaceWithProjectAndDocumentsAsync();
var updater = new OptionUpdater(workspace, "/a/b/config");
var setting = FormattingSetting.Create(CSharpFormattingOptions2.NewLineForElse, "", TestAnalyzerConfigOptions.Instance, workspace.Options, updater);
setting.SetValue(false);
var updates = await updater.GetChangedEditorConfigAsync(default);
var update = Assert.Single(updates);
Assert.Equal("[*.cs]\r\ncsharp_new_line_before_else=false", update.NewText);
Assert.Equal(@"[*.cs]
csharp_new_line_before_else=false", update.NewText);
}

[Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)]
public async Task TestCodeStyleOption_BlankFile_Severity()
{
var workspace = await CreateWorkspaceWithProjectAndDocumentsAsync();
var updater = new OptionUpdater(workspace, "/a/b/config");
var ids = IDEDiagnosticIdToOptionMappingHelper.GetDiagnosticIdsForOption(CSharpCodeStyleOptions.VarWhenTypeIsApparent, LanguageNames.CSharp);
Assert.Equal(2, ids.Length);
var setting = CodeStyleSetting.Create(CSharpCodeStyleOptions.VarWhenTypeIsApparent,
"",
TestAnalyzerConfigOptions.Instance,
workspace.Options,
updater);
setting.ChangeSeverity(DiagnosticSeverity.Error);
var updates = await updater.GetChangedEditorConfigAsync(default);
var update = Assert.Single(updates);
var value = setting.Value?.ToString().ToLower();
var expected = $@"[*.cs]
csharp_style_var_when_type_is_apparent={value}
dotnet_diagnostic.{ids[0]}.severity=error
dotnet_diagnostic.{ids[1]}.severity=error";
var actual = update.NewText;
Assert.Equal(expected, actual);
}

[Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)]
public async Task TestCodeStyleOption_BlankFile_Value()
{
var workspace = await CreateWorkspaceWithProjectAndDocumentsAsync();
var updater = new OptionUpdater(workspace, "/a/b/config");
var ids = IDEDiagnosticIdToOptionMappingHelper.GetDiagnosticIdsForOption(CSharpCodeStyleOptions.VarWhenTypeIsApparent, LanguageNames.CSharp);
Assert.Equal(2, ids.Length);
var setting = CodeStyleSetting.Create(CSharpCodeStyleOptions.VarWhenTypeIsApparent,
"",
TestAnalyzerConfigOptions.Instance,
workspace.Options,
updater);
setting.ChangeValue(0);
var updates = await updater.GetChangedEditorConfigAsync(default);
var update = Assert.Single(updates);
var expected = $@"[*.cs]
csharp_style_var_when_type_is_apparent=true
dotnet_diagnostic.{ids[0]}.severity=silent
dotnet_diagnostic.{ids[1]}.severity=silent";
var actual = update.NewText;
Assert.Equal(expected, actual);
}
}
}
Loading