Skip to content

Commit

Permalink
Ide analyzer options (#58765)
Browse files Browse the repository at this point in the history
* IDE analyzer options

* Feedback

* Fix
  • Loading branch information
tmat authored Feb 11, 2022
1 parent 629ac26 commit 040d4e8
Show file tree
Hide file tree
Showing 30 changed files with 211 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,7 @@ protected override void InitializeWorker(AnalysisContext context)

private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
{
#if CODE_STYLE
var fadeCode = true;
#else
var fadeCode = context.GetOption(Fading.FadingOptions.FadeOutUnreachableCode, LanguageNames.CSharp);
#endif
var fadeCode = context.GetIdeOptions().FadeOutUnreachableCode;
var semanticModel = context.SemanticModel;
var cancellationToken = context.CancellationToken;

Expand Down
41 changes: 41 additions & 0 deletions src/Analyzers/Core/Analyzers/Helpers/AnalyzerHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,49 @@

namespace Microsoft.CodeAnalysis.Diagnostics
{
internal readonly record struct IdeAnalyzerOptions(
bool FadeOutUnusedImports,
bool FadeOutUnreachableCode,
bool ReportInvalidPlaceholdersInStringDotFormatCalls,
bool ReportInvalidRegexPatterns)
{
public static readonly IdeAnalyzerOptions CodeStyleDefault = new(
FadeOutUnusedImports: false,
FadeOutUnreachableCode: false,
ReportInvalidPlaceholdersInStringDotFormatCalls: true,
ReportInvalidRegexPatterns: true);
}

internal static partial class AnalyzerHelper
{
public static IdeAnalyzerOptions GetIdeOptions(this SyntaxTreeAnalysisContext context)
#if CODE_STYLE
=> IdeAnalyzerOptions.CodeStyleDefault;
#else
=> (context.Options is WorkspaceAnalyzerOptions workspaceOptions) ? workspaceOptions.GetIdeOptions(context.Tree.Options.Language) : IdeAnalyzerOptions.CodeStyleDefault;
#endif

public static IdeAnalyzerOptions GetIdeOptions(this OperationAnalysisContext context)
#if CODE_STYLE
=> IdeAnalyzerOptions.CodeStyleDefault;
#else
=> (context.Options is WorkspaceAnalyzerOptions workspaceOptions) ? workspaceOptions.GetIdeOptions(context.Operation.Language) : IdeAnalyzerOptions.CodeStyleDefault;
#endif

public static IdeAnalyzerOptions GetIdeOptions(this SyntaxNodeAnalysisContext context)
#if CODE_STYLE
=> IdeAnalyzerOptions.CodeStyleDefault;
#else
=> (context.Options is WorkspaceAnalyzerOptions workspaceOptions) ? workspaceOptions.GetIdeOptions(context.Node.Language) : IdeAnalyzerOptions.CodeStyleDefault;
#endif

public static IdeAnalyzerOptions GetIdeOptions(this SemanticModelAnalysisContext context)
#if CODE_STYLE
=> IdeAnalyzerOptions.CodeStyleDefault;
#else
=> (context.Options is WorkspaceAnalyzerOptions workspaceOptions) ? workspaceOptions.GetIdeOptions(context.SemanticModel.Language) : IdeAnalyzerOptions.CodeStyleDefault;
#endif

public static T GetOption<T>(this SemanticModelAnalysisContext context, Option2<T> option)
{
var analyzerOptions = context.Options;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
// for us appropriately.
unnecessaryImports = MergeImports(unnecessaryImports);

var fadeOut = ShouldFade(context.Options, tree, language, cancellationToken);
var fadeOut = context.GetIdeOptions().FadeOutUnusedImports;

DiagnosticDescriptor descriptor;
if (GeneratedCodeUtilities.IsGeneratedCode(tree, IsRegularCommentOrDocComment, cancellationToken))
Expand All @@ -164,15 +164,6 @@ private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
context.ReportDiagnostic(diagnostic);
}
}

static bool ShouldFade(AnalyzerOptions options, SyntaxTree tree, string language, CancellationToken cancellationToken)
{
#if CODE_STYLE
return true;
#else
return options.GetOption(Fading.FadingOptions.FadeOutUnusedImports, language, tree, cancellationToken);
#endif
}
}

private IEnumerable<TextSpan> GetContiguousSpans(ImmutableArray<SyntaxNode> nodes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,7 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol for
return;
}

var option = context.GetOption(
ValidateFormatStringOption.ReportInvalidPlaceholdersInStringDotFormatCalls,
context.SemanticModel.Language);
if (option == false)
if (!context.GetIdeOptions().ReportInvalidPlaceholdersInStringDotFormatCalls)
{
return;
}
Expand Down
6 changes: 3 additions & 3 deletions src/EditorFeatures/CSharpTest/AddUsing/AddUsingNuGetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Microsoft.CodeAnalysis.CSharp.AddImport;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.SymbolSearch;
Expand All @@ -35,9 +36,8 @@ public class AddUsingNuGetTests : AbstractAddUsingTests

protected override void InitializeWorkspace(TestWorkspace workspace, TestParameters parameters)
{
workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options
.WithChangedOption(SymbolSearchOptions.SuggestForTypesInNuGetPackages, LanguageNames.CSharp, true)
.WithChangedOption(SymbolSearchOptions.SuggestForTypesInReferenceAssemblies, LanguageNames.CSharp, true)));
workspace.GlobalOptions.SetGlobalOption(new OptionKey(SymbolSearchOptionsStorage.SearchNuGetPackages, LanguageNames.CSharp), true);
workspace.GlobalOptions.SetGlobalOption(new OptionKey(SymbolSearchOptionsStorage.SearchReferenceAssemblies, LanguageNames.CSharp), true);
}

internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ internal static class CodeActionOptionsStorage
{
internal static CodeActionOptions GetCodeActionOptions(this IGlobalOptionService globalOptions, string language, bool isBlocking)
=> new(
IsBlocking: isBlocking,
SearchReferenceAssemblies: globalOptions.GetOption(SymbolSearchOptions.SuggestForTypesInReferenceAssemblies, language),
HideAdvancedMembers: globalOptions.GetOption(CompletionOptionsStorage.HideAdvancedMembers, language));
SearchOptions: globalOptions.GetSymbolSearchOptions(language),
HideAdvancedMembers: globalOptions.GetOption(CompletionOptionsStorage.HideAdvancedMembers, language),
IsBlocking: isBlocking);
}
}
26 changes: 26 additions & 0 deletions src/EditorFeatures/Core/SymbolSearch/SymbolSearchOptionsStorage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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 Microsoft.CodeAnalysis.Options;

namespace Microsoft.CodeAnalysis.SymbolSearch
{
internal static class SymbolSearchOptionsStorage
{
internal static SymbolSearchOptions GetSymbolSearchOptions(this IGlobalOptionService globalOptions, string language)
=> new(
SearchReferenceAssemblies: globalOptions.GetOption(SearchReferenceAssemblies, language),
SearchNuGetPackages: globalOptions.GetOption(SearchNuGetPackages, language));

private const string FeatureName = "SymbolSearchOptions";

public static PerLanguageOption2<bool> SearchReferenceAssemblies =
new(FeatureName, "SuggestForTypesInReferenceAssemblies", SymbolSearchOptions.Default.SearchReferenceAssemblies,
storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.SuggestForTypesInReferenceAssemblies"));

public static PerLanguageOption2<bool> SearchNuGetPackages =
new(FeatureName, "SuggestForTypesInNuGetPackages", SymbolSearchOptions.Default.SearchNuGetPackages,
storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.SuggestForTypesInNuGetPackages"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Imports Microsoft.CodeAnalysis.CodeActions
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.Packaging
Imports Microsoft.CodeAnalysis.Shared.Utilities
Imports Microsoft.CodeAnalysis.SymbolSearch
Expand All @@ -25,9 +26,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeActions.AddImp
ImmutableArray.Create(New PackageSource(PackageSourceHelper.NugetOrgSourceName, "http://nuget.org"))

Protected Overrides Sub InitializeWorkspace(workspace As TestWorkspace, parameters As TestParameters)
workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options.
WithChangedOption(SymbolSearchOptions.SuggestForTypesInNuGetPackages, LanguageNames.VisualBasic, True).
WithChangedOption(SymbolSearchOptions.SuggestForTypesInReferenceAssemblies, LanguageNames.VisualBasic, True)))
workspace.GlobalOptions.SetGlobalOption(New OptionKey(SymbolSearchOptionsStorage.SearchNuGetPackages, LanguageNames.VisualBasic), True)
workspace.GlobalOptions.SetGlobalOption(New OptionKey(SymbolSearchOptionsStorage.SearchReferenceAssemblies, LanguageNames.VisualBasic), True)
End Sub

Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@
// 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.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.Packaging;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.SymbolSearch;
Expand All @@ -20,15 +16,15 @@ internal abstract partial class AbstractAddImportCodeFixProvider : CodeFixProvid
{
private const int MaxResults = 5;

private readonly IPackageInstallerService _packageInstallerService;
private readonly ISymbolSearchService _symbolSearchService;
private readonly IPackageInstallerService? _packageInstallerService;
private readonly ISymbolSearchService? _symbolSearchService;

/// <summary>
/// Values for these parameters can be provided (during testing) for mocking purposes.
/// </summary>
protected AbstractAddImportCodeFixProvider(
IPackageInstallerService packageInstallerService = null,
ISymbolSearchService symbolSearchService = null)
IPackageInstallerService? packageInstallerService = null,
ISymbolSearchService? symbolSearchService = null)
{
_packageInstallerService = packageInstallerService;
_symbolSearchService = symbolSearchService;
Expand All @@ -42,7 +38,7 @@ protected AbstractAddImportCodeFixProvider(
private protected override CodeActionRequestPriority ComputeRequestPriority()
=> CodeActionRequestPriority.High;

public sealed override FixAllProvider GetFixAllProvider()
public sealed override FixAllProvider? GetFixAllProvider()
{
// Currently Fix All is not supported for this provider
// https://github.com/dotnet/roslyn/issues/34457
Expand All @@ -56,30 +52,34 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
var cancellationToken = context.CancellationToken;
var diagnostics = context.Diagnostics;

var addImportService = document.GetLanguageService<IAddImportFeatureService>();
var addImportService = document.GetRequiredLanguageService<IAddImportFeatureService>();
var services = document.Project.Solution.Workspace.Services;

var searchOptions = context.Options.SearchOptions;

var solution = document.Project.Solution;
var symbolSearchService = _symbolSearchService ?? services.GetRequiredService<ISymbolSearchService>();

var searchNuGetPackages = solution.Options.GetOption(SymbolSearchOptions.SuggestForTypesInNuGetPackages, document.Project.Language);
var installerService = searchOptions.SearchNuGetPackages ?
_packageInstallerService ?? services.GetService<IPackageInstallerService>() : null;

var packageSources = installerService?.IsEnabled(document.Project.Id) == true
? installerService.TryGetPackageSources()
: ImmutableArray<PackageSource>.Empty;

if (packageSources.IsEmpty)
{
searchOptions = searchOptions with { SearchNuGetPackages = false };
}

var placement = await AddImportPlacementOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false);

var options = new AddImportOptions(
context.Options.SearchReferenceAssemblies,
var addImportOptions = new AddImportOptions(
searchOptions,
context.Options.HideAdvancedMembers,
placement);

var symbolSearchService = options.SearchReferenceAssemblies || searchNuGetPackages
? _symbolSearchService ?? solution.Workspace.Services.GetService<ISymbolSearchService>()
: null;

var installerService = GetPackageInstallerService(document);
var packageSources = searchNuGetPackages && symbolSearchService != null && installerService?.IsEnabled(document.Project.Id) == true
? installerService.TryGetPackageSources()
: ImmutableArray<PackageSource>.Empty;

var fixesForDiagnostic = await addImportService.GetFixesForDiagnosticsAsync(
document, span, diagnostics, MaxResults, symbolSearchService, options, packageSources, cancellationToken).ConfigureAwait(false);
document, span, diagnostics, MaxResults, symbolSearchService, addImportOptions, packageSources, cancellationToken).ConfigureAwait(false);

foreach (var (diagnostic, fixes) in fixesForDiagnostic)
{
Expand All @@ -88,8 +88,5 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
context.RegisterFixes(codeActions, diagnostic);
}
}

private IPackageInstallerService GetPackageInstallerService(Document document)
=> _packageInstallerService ?? document.Project.Solution.Workspace.Services.GetService<IPackageInstallerService>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.AddImport
{
[DataContract]
internal readonly record struct AddImportOptions(
[property: DataMember(Order = 0)] bool SearchReferenceAssemblies,
[property: DataMember(Order = 0)] SymbolSearchOptions SearchOptions,
[property: DataMember(Order = 1)] bool HideAdvancedMembers,
[property: DataMember(Order = 2)] AddImportPlacementOptions Placement);

Expand Down
6 changes: 0 additions & 6 deletions src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,6 @@ public SymbolReferenceFinder(
_symbolSearchService = symbolSearchService;
_options = options;
_packageSources = packageSources;

if (options.SearchReferenceAssemblies || packageSources.Length > 0)
{
Contract.ThrowIfNull(symbolSearchService);
}

_syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();

_namespacesInScope = GetNamespacesInScope(cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private async Task FindNugetOrReferenceAssemblyTypeReferencesWorkerAsync(
ArrayBuilder<Reference> allReferences, TSimpleNameSyntax nameNode,
string name, int arity, bool isAttributeSearch, CancellationToken cancellationToken)
{
if (_options.SearchReferenceAssemblies)
if (_options.SearchOptions.SearchReferenceAssemblies)
{
cancellationToken.ThrowIfCancellationRequested();
await FindReferenceAssemblyTypeReferencesAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,10 @@ protected async Task<ImmutableArray<CodeAction>> GetAddPackagesCodeActionsAsync(
var symbolSearchService = _symbolSearchService ?? workspaceServices.GetService<ISymbolSearchService>();
var installerService = _packageInstallerService ?? workspaceServices.GetService<IPackageInstallerService>();

var language = document.Project.Language;

var options = document.Project.Solution.Options;
var searchNugetPackages = options.GetOption(
SymbolSearchOptions.SuggestForTypesInNuGetPackages, language);

var codeActions = ArrayBuilder<CodeAction>.GetInstance();
if (symbolSearchService != null &&
installerService != null &&
searchNugetPackages &&
context.Options.SearchOptions.SearchNuGetPackages &&
installerService.IsEnabled(document.Project.Id))
{
var packageSources = PackageSourceHelper.GetPackageSources(installerService.TryGetPackageSources());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public async Task<AddMissingImportsAnalysisResult> AnalyzeAsync(Document documen
var packageSources = ImmutableArray<PackageSource>.Empty;

var addImportOptions = new AddImportOptions(
SearchReferenceAssemblies: true,
SearchOptions: new(SearchReferenceAssemblies: true, SearchNuGetPackages: false),
HideAdvancedMembers: options.HideAdvancedMembers,
Placement: options.Placement);

Expand Down
1 change: 0 additions & 1 deletion src/Features/Core/Portable/Diagnostics/AnalyzerHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ namespace Microsoft.CodeAnalysis.Diagnostics
{
internal static partial class AnalyzerHelper
{

// These are the error codes of the compiler warnings.
// Keep the ids the same so that de-duplication against compiler errors
// works in the error list (after a build).
Expand Down
Loading

0 comments on commit 040d4e8

Please sign in to comment.