提交 bf84a3bf 编写于 作者: C Cyrus Najmabadi

Simplify impl of 'prefer predefined type'

上级 351472b6
......@@ -12,18 +12,19 @@ namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.Analyzers
internal class CSharpPreferFrameworkTypeDiagnosticAnalyzer :
PreferFrameworkTypeDiagnosticAnalyzerBase<SyntaxKind, ExpressionSyntax, PredefinedTypeSyntax>
{
protected override ImmutableArray<SyntaxKind> SyntaxKindsOfInterest =>
protected override ImmutableArray<SyntaxKind> SyntaxKindsOfInterest { get; } =
ImmutableArray.Create(SyntaxKind.PredefinedType);
///<remarks>
/// every predefined type keyword except `void` can be replaced by its framework type in code.
///</remarks>
protected override bool IsPredefinedTypeReplaceableWithFrameworkType(PredefinedTypeSyntax node) =>
node.Keyword.Kind() != SyntaxKind.VoidKeyword;
protected override bool IsPredefinedTypeReplaceableWithFrameworkType(PredefinedTypeSyntax node)
=> node.Keyword.Kind() != SyntaxKind.VoidKeyword;
protected override bool IsInMemberAccessOrCrefReferenceContext(ExpressionSyntax node) =>
node.IsInMemberAccessContext() || node.InsideCrefReference();
protected override bool IsInMemberAccessOrCrefReferenceContext(ExpressionSyntax node)
=> node.IsInMemberAccessContext() || node.InsideCrefReference();
protected override string GetLanguageName() => LanguageNames.CSharp;
protected override string GetLanguageName()
=> LanguageNames.CSharp;
}
}
......@@ -9,42 +9,47 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeFixes.PreferFrameworkType
{
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic,
Name = PredefinedCodeFixProviderNames.PreferFrameworkType), Shared]
internal class PreferFrameworkTypeCodeFixProvider : CodeFixProvider
internal class PreferFrameworkTypeCodeFixProvider : SyntaxEditorBasedCodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
IDEDiagnosticIds.PreferFrameworkTypeInDeclarationsDiagnosticId,
IDEDiagnosticIds.PreferFrameworkTypeInMemberAccessDiagnosticId);
public override FixAllProvider GetFixAllProvider() => BatchFixAllProvider.Instance;
public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(IDEDiagnosticIds.PreferFrameworkTypeDiagnosticId);
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(
new PreferFrameworkTypeCodeAction(
FeaturesResources.Use_framework_type,
c => CreateChangedDocumentAsync(context.Document, context.Span, c)),
c => this.FixAsync(context.Document, context.Diagnostics[0], c)),
context.Diagnostics);
return SpecializedTasks.EmptyTask;
}
private async Task<Document> CreateChangedDocumentAsync(Document document, TextSpan span, CancellationToken cancellationToken)
protected override async Task FixAllAsync(
Document document, ImmutableArray<Diagnostic> diagnostics,
SyntaxEditor editor, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var node = root.FindNode(span, findInsideTrivia: true, getInnermostNodeForTie: true);
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var generator = document.GetLanguageService<SyntaxGenerator>();
var typeSymbol = (ITypeSymbol)semanticModel.GetSymbolInfo(node, cancellationToken).Symbol;
var replacementNode = generator.TypeExpression(typeSymbol).WithTriviaFrom(node);
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
return document.WithSyntaxRoot(root.ReplaceNode(node, replacementNode));
foreach (var diagnostic in diagnostics)
{
var node = diagnostic.Location.FindNode(
findInsideTrivia: true, getInnermostNodeForTie: true, cancellationToken);
var typeSymbol = semanticModel.GetSymbolInfo(node, cancellationToken).Symbol as ITypeSymbol;
if (typeSymbol != null)
{
var replacementNode = generator.TypeExpression(typeSymbol).WithTriviaFrom(node);
editor.ReplaceNode(node, replacementNode);
}
}
}
private class PreferFrameworkTypeCodeAction : CodeAction.DocumentChangeAction
......
......@@ -17,8 +17,9 @@ internal static class IDEDiagnosticIds
public const string AddBracesDiagnosticId = "IDE0011";
public const string PreferIntrinsicPredefinedTypeInDeclarationsDiagnosticId = "IDE0012";
public const string PreferIntrinsicPredefinedTypeInMemberAccessDiagnosticId = "IDE0013";
public const string PreferFrameworkTypeInDeclarationsDiagnosticId = "IDE0014";
public const string PreferFrameworkTypeInMemberAccessDiagnosticId = "IDE0015";
public const string PreferFrameworkTypeDiagnosticId = "IDE0014";
// Not used any more. All 'prefer framework type' diagnostics go through the above ID
// public const string PreferFrameworkTypeInMemberAccessDiagnosticId = "IDE0015";
public const string UseThrowExpressionDiagnosticId = "IDE0016";
public const string UseObjectInitializerDiagnosticId = "IDE0017";
public const string InlineDeclarationDiagnosticId = "IDE0018";
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.Diagnostics.PreferFrameworkType
{
internal abstract class PreferFrameworkTypeDiagnosticAnalyzerBase<TSyntaxKind, TExpressionSyntax, TPredefinedTypeSyntax> :
DiagnosticAnalyzer, IBuiltInAnalyzer
AbstractCodeStyleDiagnosticAnalyzer
where TSyntaxKind : struct
where TExpressionSyntax : SyntaxNode
where TPredefinedTypeSyntax : TExpressionSyntax
{
private static readonly LocalizableString s_preferFrameworkTypeMessage =
new LocalizableResourceString(nameof(FeaturesResources.Use_framework_type),
FeaturesResources.ResourceManager, typeof(FeaturesResources));
private static readonly LocalizableString s_preferFrameworkTypeTitle =
new LocalizableResourceString(nameof(FeaturesResources.Use_framework_type),
FeaturesResources.ResourceManager, typeof(FeaturesResources));
private static readonly DiagnosticDescriptor s_descriptorPreferFrameworkTypeInDeclarations =
new DiagnosticDescriptor(
IDEDiagnosticIds.PreferFrameworkTypeInDeclarationsDiagnosticId,
s_preferFrameworkTypeTitle,
s_preferFrameworkTypeMessage,
DiagnosticCategory.Style,
DiagnosticSeverity.Hidden,
isEnabledByDefault: true);
private static readonly DiagnosticDescriptor s_descriptorPreferFrameworkTypeInMemberAccess =
new DiagnosticDescriptor(
IDEDiagnosticIds.PreferFrameworkTypeInMemberAccessDiagnosticId,
s_preferFrameworkTypeTitle,
s_preferFrameworkTypeMessage,
DiagnosticCategory.Style,
DiagnosticSeverity.Hidden,
isEnabledByDefault: true);
private PerLanguageOption<CodeStyleOption<bool>> GetOptionForDeclarationContext =>
CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInDeclaration;
private PerLanguageOption<CodeStyleOption<bool>> GetOptionForMemberAccessContext =>
CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInMemberAccess;
public bool OpenFileOnly(Workspace workspace)
protected PreferFrameworkTypeDiagnosticAnalyzerBase()
: base(IDEDiagnosticIds.PreferFrameworkTypeDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Use_framework_type), FeaturesResources.ResourceManager, typeof(FeaturesResources)),
new LocalizableResourceString(nameof(FeaturesResources.Use_framework_type), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
{
}
private static PerLanguageOption<CodeStyleOption<bool>> GetOptionForDeclarationContext
=> CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInDeclaration;
private static PerLanguageOption<CodeStyleOption<bool>> GetOptionForMemberAccessContext
=> CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInMemberAccess;
public override bool OpenFileOnly(Workspace workspace)
{
var preferTypeKeywordInDeclarationOption = workspace.Options.GetOption(
CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInDeclaration, GetLanguageName()).Notification;
......@@ -56,24 +35,16 @@ public bool OpenFileOnly(Workspace workspace)
return !(preferTypeKeywordInDeclarationOption == NotificationOption.Warning || preferTypeKeywordInDeclarationOption == NotificationOption.Error ||
preferTypeKeywordInMemberAccessOption == NotificationOption.Warning || preferTypeKeywordInMemberAccessOption == NotificationOption.Error);
}
public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
protected abstract string GetLanguageName();
public DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
s_descriptorPreferFrameworkTypeInDeclarations, s_descriptorPreferFrameworkTypeInMemberAccess);
protected abstract ImmutableArray<TSyntaxKind> SyntaxKindsOfInterest { get; }
protected abstract bool IsPredefinedTypeReplaceableWithFrameworkType(TPredefinedTypeSyntax node);
protected abstract bool IsInMemberAccessOrCrefReferenceContext(TExpressionSyntax node);
public sealed override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKindsOfInterest);
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
}
protected sealed override void InitializeWorker(AnalysisContext context)
=> context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKindsOfInterest);
protected void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
......@@ -111,15 +82,9 @@ protected void AnalyzeNode(SyntaxNodeAnalysisContext context)
}
// earlier we did a context insensitive check to see if this style was preferred in *any* context at all.
// now, we have to make a context sensitive check to see if options settings for our context requires us to report a diagnostic.
if (ShouldReportDiagnostic(predefinedTypeNode, optionSet, language, out var diagnosticId, out var diagnosticSeverity))
if (ShouldReportDiagnostic(predefinedTypeNode, optionSet, language, out var diagnosticSeverity))
{
var descriptor = new DiagnosticDescriptor(diagnosticId,
s_preferFrameworkTypeTitle,
s_preferFrameworkTypeMessage,
DiagnosticCategory.Style,
diagnosticSeverity,
isEnabledByDefault: true);
var descriptor = GetDescriptorWithSeverity(diagnosticSeverity);
context.ReportDiagnostic(Diagnostic.Create(descriptor, predefinedTypeNode.GetLocation()));
}
}
......@@ -127,31 +92,23 @@ protected void AnalyzeNode(SyntaxNodeAnalysisContext context)
/// <summary>
/// Detects the context of this occurrence of predefined type and determines if we should report it.
/// </summary>
private bool ShouldReportDiagnostic(TPredefinedTypeSyntax predefinedTypeNode, OptionSet optionSet, string language,
out string diagnosticId, out DiagnosticSeverity severity)
private bool ShouldReportDiagnostic(
TPredefinedTypeSyntax predefinedTypeNode, OptionSet optionSet,
string language, out DiagnosticSeverity severity)
{
CodeStyleOption<bool> optionValue;
// we have a predefined type syntax that is either in a member access context or a declaration context.
// check the appropriate option and determine if we should report a diagnostic.
if (IsInMemberAccessOrCrefReferenceContext(predefinedTypeNode))
{
diagnosticId = IDEDiagnosticIds.PreferFrameworkTypeInMemberAccessDiagnosticId;
optionValue = optionSet.GetOption(GetOptionForMemberAccessContext, language);
}
else
{
diagnosticId = IDEDiagnosticIds.PreferFrameworkTypeInDeclarationsDiagnosticId;
optionValue = optionSet.GetOption(GetOptionForDeclarationContext, language);
}
var optionValue = IsInMemberAccessOrCrefReferenceContext(predefinedTypeNode)
? optionSet.GetOption(GetOptionForMemberAccessContext, language)
: optionSet.GetOption(GetOptionForDeclarationContext, language);
severity = optionValue.Notification.Value;
return OptionSettingPrefersFrameworkType(optionValue, severity);
}
private bool IsStylePreferred(OptionSet optionSet, string language) =>
IsFrameworkTypePreferred(optionSet, GetOptionForDeclarationContext, language) ||
IsFrameworkTypePreferred(optionSet, GetOptionForMemberAccessContext, language);
private bool IsStylePreferred(OptionSet optionSet, string language)
=> IsFrameworkTypePreferred(optionSet, GetOptionForDeclarationContext, language) ||
IsFrameworkTypePreferred(optionSet, GetOptionForMemberAccessContext, language);
private bool IsFrameworkTypePreferred(OptionSet optionSet, PerLanguageOption<CodeStyleOption<bool>> option, string language)
{
......@@ -163,7 +120,7 @@ private bool IsFrameworkTypePreferred(OptionSet optionSet, PerLanguageOption<Cod
/// checks if style is preferred and the enforcement is not None.
/// </summary>
/// <remarks>if predefined type is not preferred, it implies the preference is framework type.</remarks>
private static bool OptionSettingPrefersFrameworkType(CodeStyleOption<bool> optionValue, DiagnosticSeverity severity) =>
!optionValue.Value && severity != DiagnosticSeverity.Hidden;
private static bool OptionSettingPrefersFrameworkType(CodeStyleOption<bool> optionValue, DiagnosticSeverity severity)
=> !optionValue.Value && severity != DiagnosticSeverity.Hidden;
}
}
......@@ -10,11 +10,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Diagnostics.Analyzers
Friend Class VisualBasicPreferFrameworkTypeDiagnosticAnalyzer
Inherits PreferFrameworkTypeDiagnosticAnalyzerBase(Of SyntaxKind, ExpressionSyntax, PredefinedTypeSyntax)
Protected Overrides ReadOnly Property SyntaxKindsOfInterest As ImmutableArray(Of SyntaxKind)
Get
Return ImmutableArray.Create(SyntaxKind.PredefinedType)
End Get
End Property
Protected Overrides ReadOnly Property SyntaxKindsOfInterest As ImmutableArray(Of SyntaxKind) =
ImmutableArray.Create(SyntaxKind.PredefinedType)
Protected Overrides Function IsInMemberAccessOrCrefReferenceContext(node As ExpressionSyntax) As Boolean
Return node.IsInMemberAccessContext() OrElse node.InsideCrefReference()
......@@ -52,5 +49,4 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Diagnostics.Analyzers
Return False
End Function
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册