diff --git a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs index bce348ef1a3667bd1562cc9a0da84b88a9ccf1f2..01ef29695c82e91f50fb1970e82fe9e90625ff0f 100644 --- a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs +++ b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs @@ -3,84 +3,40 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Reflection; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CodeFixes { - using TypeInfo = System.Reflection.TypeInfo; - internal partial class CodeFixService { private class ProjectCodeFixProvider + : AbstractProjectExtensionProvider { - private readonly AnalyzerReference _reference; - private ImmutableDictionary> _fixersPerLanguage; - public ProjectCodeFixProvider(AnalyzerReference reference) + : base(reference) { - _reference = reference; - _fixersPerLanguage = ImmutableDictionary>.Empty; } - public ImmutableArray GetFixers(string language) - => ImmutableInterlocked.GetOrAdd(ref _fixersPerLanguage, language, CreateFixers); + protected override bool SupportsLanguage(ExportCodeFixProviderAttribute exportAttribute, string language) + { + return exportAttribute.Languages == null + || exportAttribute.Languages.Length == 0 + || exportAttribute.Languages.Contains(language); + } - private ImmutableArray CreateFixers(string language) + protected override bool TryGetExtensionsFromReference(AnalyzerReference reference, out ImmutableArray extensions) { // check whether the analyzer reference knows how to return fixers directly. - if (_reference is ICodeFixProviderFactory codeFixProviderFactory) - { - return codeFixProviderFactory.GetFixers(); - } - - // otherwise, see whether we can pick it up from reference itself - if (!(_reference is AnalyzerFileReference analyzerFileReference)) - { - return ImmutableArray.Empty; - } - - using var builderDisposer = ArrayBuilder.GetInstance(out var builder); - - try - { - var analyzerAssembly = analyzerFileReference.GetAssembly(); - var typeInfos = analyzerAssembly.DefinedTypes; - - foreach (var typeInfo in typeInfos) - { - if (typeInfo.IsSubclassOf(typeof(CodeFixProvider))) - { - try - { - var attribute = typeInfo.GetCustomAttribute(); - if (attribute != null) - { - if (attribute.Languages == null || - attribute.Languages.Length == 0 || - attribute.Languages.Contains(language)) - { - builder.Add((CodeFixProvider)Activator.CreateInstance(typeInfo.AsType())); - } - } - } - catch - { - } - } - } - } - catch + if (reference is ICodeFixProviderFactory codeFixProviderFactory) { - // REVIEW: is the below message right? - // NOTE: We could report "unable to load analyzer" exception here but it should have been already reported by DiagnosticService. + extensions = codeFixProviderFactory.GetFixers(); + return true; } - return builder.ToImmutable(); + extensions = default; + return false; } } } diff --git a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs index 41a898eb55654e2beadddae7af28c0f1a754603b..42292f08e6755eb69533daf135b854139ca6d2e0 100644 --- a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs +++ b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs @@ -807,7 +807,7 @@ static ImmutableArray GetConfigurationFixProviders(Li foreach (var reference in project.AnalyzerReferences) { var projectCodeFixerProvider = _analyzerReferenceToFixersMap.GetValue(reference, _createProjectCodeFixProvider); - foreach (var fixer in projectCodeFixerProvider.GetFixers(project.Language)) + foreach (var fixer in projectCodeFixerProvider.GetExtensions(project.Language)) { var fixableIds = this.GetFixableDiagnosticIds(fixer, extensionManager); foreach (var id in fixableIds) diff --git a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs index c19d331e99bc1e0ebbfa38f224d40d288bb6a62c..90fb2acc0719c9db123e400743d036d13b0d1322 100644 --- a/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/CodeRefactoringService.cs @@ -9,7 +9,6 @@ using System.Collections.Immutable; using System.Composition; using System.Linq; -using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -229,7 +228,7 @@ ImmutableArray ComputeProjectRefactorings(Project proje foreach (var reference in project.AnalyzerReferences) { var projectCodeRefactoringProvider = _analyzerReferenceToRefactoringsMap.GetValue(reference, _createProjectCodeRefactoringsProvider); - foreach (var refactoring in projectCodeRefactoringProvider.GetRefactorings(project.Language)) + foreach (var refactoring in projectCodeRefactoringProvider.GetExtensions(project.Language)) { builder.Add(refactoring); } @@ -240,70 +239,31 @@ ImmutableArray ComputeProjectRefactorings(Project proje } private class ProjectCodeRefactoringProvider + : AbstractProjectExtensionProvider { - private readonly AnalyzerReference _reference; - private ImmutableDictionary> _refactoringsPerLanguage; - public ProjectCodeRefactoringProvider(AnalyzerReference reference) + : base(reference) { - _reference = reference; - _refactoringsPerLanguage = ImmutableDictionary>.Empty; } - public ImmutableArray GetRefactorings(string language) - => ImmutableInterlocked.GetOrAdd(ref _refactoringsPerLanguage, language, (language, provider) => provider.CreateRefactorings(language), this); + protected override bool SupportsLanguage(ExportCodeRefactoringProviderAttribute exportAttribute, string language) + { + return exportAttribute.Languages == null + || exportAttribute.Languages.Length == 0 + || exportAttribute.Languages.Contains(language); + } - private ImmutableArray CreateRefactorings(string language) + protected override bool TryGetExtensionsFromReference(AnalyzerReference reference, out ImmutableArray extensions) { // check whether the analyzer reference knows how to return fixers directly. - if (_reference is ICodeRefactoringProviderFactory codeRefactoringProviderFactory) - { - return codeRefactoringProviderFactory.GetRefactorings(); - } - - // otherwise, see whether we can pick it up from reference itself - if (!(_reference is AnalyzerFileReference analyzerFileReference)) - { - return ImmutableArray.Empty; - } - - var builder = ArrayBuilder.GetInstance(); - - try - { - var analyzerAssembly = analyzerFileReference.GetAssembly(); - var typeInfos = analyzerAssembly.DefinedTypes; - - foreach (var typeInfo in typeInfos) - { - if (typeInfo.IsSubclassOf(typeof(CodeRefactoringProvider))) - { - try - { - var attribute = typeInfo.GetCustomAttribute(); - if (attribute != null) - { - if (attribute.Languages == null || - attribute.Languages.Length == 0 || - attribute.Languages.Contains(language)) - { - builder.Add((CodeRefactoringProvider)Activator.CreateInstance(typeInfo.AsType())!); - } - } - } - catch - { - } - } - } - } - catch + if (reference is ICodeRefactoringProviderFactory codeRefactoringProviderFactory) { - // REVIEW: is the below message right? - // NOTE: We could report "unable to load analyzer" exception here but it should have been already reported by DiagnosticService. + extensions = codeRefactoringProviderFactory.GetRefactorings(); + return true; } - return builder.ToImmutableAndFree(); + extensions = default; + return false; } } } diff --git a/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider`2.cs b/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider`2.cs new file mode 100644 index 0000000000000000000000000000000000000000..d316dd1943b659244f2ab9a94f41f20cad9a2971 --- /dev/null +++ b/src/Features/Core/Portable/Common/AbstractProjectExtensionProvider`2.cs @@ -0,0 +1,89 @@ +// 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.Immutable; +using System.Reflection; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis +{ + internal abstract class AbstractProjectExtensionProvider + where TExportAttribute : Attribute + { + private readonly AnalyzerReference _reference; + private ImmutableDictionary> _extensionsPerLanguage; + + public AbstractProjectExtensionProvider(AnalyzerReference reference) + { + _reference = reference; + _extensionsPerLanguage = ImmutableDictionary>.Empty; + } + + public ImmutableArray GetExtensions(string language) + { + return ImmutableInterlocked.GetOrAdd(ref _extensionsPerLanguage, language, (language, provider) => provider.CreateExtensions(language), this); + } + + protected virtual bool SupportsLanguage(TExportAttribute exportAttribute, string language) + { + return true; + } + + protected virtual bool TryGetExtensionsFromReference(AnalyzerReference reference, out ImmutableArray extensions) + { + extensions = default; + return false; + } + + private ImmutableArray CreateExtensions(string language) + { + // check whether the analyzer reference knows how to return fixers directly. + if (TryGetExtensionsFromReference(_reference, out var extensions)) + { + return extensions; + } + + // otherwise, see whether we can pick it up from reference itself + if (!(_reference is AnalyzerFileReference analyzerFileReference)) + { + return ImmutableArray.Empty; + } + + using var _ = ArrayBuilder.GetInstance(out var builder); + + try + { + var analyzerAssembly = analyzerFileReference.GetAssembly(); + var typeInfos = analyzerAssembly.DefinedTypes; + + foreach (var typeInfo in typeInfos) + { + if (typeInfo.IsSubclassOf(typeof(TExtension))) + { + try + { + var attribute = typeInfo.GetCustomAttribute(); + if (attribute is object && SupportsLanguage(attribute, language)) + { + builder.Add((TExtension)Activator.CreateInstance(typeInfo.AsType())); + } + } + catch + { + } + } + } + } + catch + { + // REVIEW: is the below message right? + // NOTE: We could report "unable to load analyzer" exception here but it should have been already reported by DiagnosticService. + } + + return builder.ToImmutable(); + } + } +}