未验证 提交 c4eb9fd1 编写于 作者: J Jinu 提交者: GitHub

Merge pull request #26611 from CyrusNajmabadi/useSyntaxEditor

Use SyntaxEditorBasedCodeFixProvider for 'qualify member access'
......@@ -2,18 +2,20 @@
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.QualifyMemberAccess;
namespace Microsoft.CodeAnalysis.CSharp.QualifyMemberAccess
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpQualifyMemberAccessDiagnosticAnalyzer : AbstractQualifyMemberAccessDiagnosticAnalyzer<SyntaxKind>
internal sealed class CSharpQualifyMemberAccessDiagnosticAnalyzer
: AbstractQualifyMemberAccessDiagnosticAnalyzer<SyntaxKind, ExpressionSyntax, SimpleNameSyntax>
{
protected override string GetLanguageName()
=> LanguageNames.CSharp;
protected override bool IsAlreadyQualifiedMemberAccess(SyntaxNode node)
protected override bool IsAlreadyQualifiedMemberAccess(ExpressionSyntax node)
=> node.IsKind(SyntaxKind.ThisExpression);
// If the member is already qualified with `base.`,
......
......@@ -192,7 +192,7 @@ private DiagnosticDescriptor GetRemoveQualificationDiagnosticDescriptor(Semantic
return null;
}
var applicableOption = AbstractQualifyMemberAccessDiagnosticAnalyzer<TLanguageKindEnum>.GetApplicableOptionFromSymbolKind(symbolInfo.Symbol.Kind);
var applicableOption = QualifyMembersHelpers.GetApplicableOptionFromSymbolKind(symbolInfo.Symbol.Kind);
var optionValue = optionSet.GetOption(applicableOption, GetLanguageName());
var severity = optionValue.Notification.Value;
......
......@@ -12,51 +12,46 @@
namespace Microsoft.CodeAnalysis.QualifyMemberAccess
{
internal abstract class AbstractQualifyMemberAccessCodeFixprovider<TSyntaxNode> : CodeFixProvider where TSyntaxNode : SyntaxNode
internal abstract class AbstractQualifyMemberAccessCodeFixprovider<TSimpleNameSyntax>
: SyntaxEditorBasedCodeFixProvider
where TSimpleNameSyntax : SyntaxNode
{
protected abstract string GetTitle();
public sealed override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(IDEDiagnosticIds.AddQualificationDiagnosticId);
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var document = context.Document;
var span = context.Span;
var cancellationToken = context.CancellationToken;
context.RegisterCodeFix(new MyCodeAction(
this.GetTitle(),
c => FixAsync(context.Document, context.Diagnostics[0], c)),
context.Diagnostics);
return Task.CompletedTask;
}
protected override async Task FixAllAsync(
Document document, ImmutableArray<Diagnostic> diagnostics,
SyntaxEditor editor, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var generator = document.GetLanguageService<SyntaxGenerator>();
var token = root.FindToken(span.Start);
if (!token.Span.IntersectsWith(span))
foreach (var diagnostic in diagnostics)
{
return;
}
var node = diagnostic.Location.FindNode(getInnermostNodeForTie: true, cancellationToken) as TSimpleNameSyntax;
if (node != null)
{
var qualifiedAccess =
generator.MemberAccessExpression(
generator.ThisExpression(),
node.WithLeadingTrivia())
.WithLeadingTrivia(node.GetLeadingTrivia());
var node = token.GetAncestor<TSyntaxNode>();
if (node == null)
{
return;
editor.ReplaceNode(node, qualifiedAccess);
}
}
var generator = document.GetLanguageService<SyntaxGenerator>();
var title = this.GetTitle();
var codeAction = new MyCodeAction(
title, c => document.ReplaceNodeAsync(node, GetReplacementSyntax(node, generator), c));
context.RegisterCodeFix(codeAction, context.Diagnostics);
}
protected abstract string GetTitle();
public override FixAllProvider GetFixAllProvider() => BatchFixAllProvider.Instance;
private static SyntaxNode GetReplacementSyntax(SyntaxNode node, SyntaxGenerator generator)
{
var qualifiedAccess =
generator.MemberAccessExpression(
generator.ThisExpression(),
node.WithLeadingTrivia())
.WithLeadingTrivia(node.GetLeadingTrivia());
return qualifiedAccess;
}
private class MyCodeAction : CodeAction.DocumentChangeAction
......
// 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.Reflection;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.Options;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.QualifyMemberAccess
{
internal abstract class AbstractQualifyMemberAccessDiagnosticAnalyzer<TLanguageKindEnum> :
AbstractCodeStyleDiagnosticAnalyzer
internal abstract class AbstractQualifyMemberAccessDiagnosticAnalyzer<
TLanguageKindEnum,
TExpressionSyntax,
TSimpleNameSyntax>
: AbstractCodeStyleDiagnosticAnalyzer
where TLanguageKindEnum : struct
where TExpressionSyntax : SyntaxNode
where TSimpleNameSyntax : TExpressionSyntax
{
protected AbstractQualifyMemberAccessDiagnosticAnalyzer()
: base(IDEDiagnosticIds.AddQualificationDiagnosticId,
......@@ -45,7 +47,7 @@ public override bool OpenFileOnly(Workspace workspace)
/// <returns>True if the member access can be qualified; otherwise, False.</returns>
protected abstract bool CanMemberAccessBeQualified(ISymbol containingSymbol, SyntaxNode node);
protected abstract bool IsAlreadyQualifiedMemberAccess(SyntaxNode node);
protected abstract bool IsAlreadyQualifiedMemberAccess(TExpressionSyntax node);
protected override void InitializeWorker(AnalysisContext context)
=> context.RegisterOperationAction(AnalyzeOperation, OperationKind.FieldReference, OperationKind.PropertyReference, OperationKind.MethodReference);
......@@ -74,7 +76,8 @@ private void AnalyzeOperation(OperationAnalysisContext context)
}
// If we can't be qualified (e.g., because we're already qualified with `base.`), we're done.
if (!CanMemberAccessBeQualified(context.ContainingSymbol, memberReference.Instance.Syntax))
var instanceSyntaxOpt = memberReference.Instance.Syntax as TExpressionSyntax;
if (!CanMemberAccessBeQualified(context.ContainingSymbol, instanceSyntaxOpt))
{
return;
}
......@@ -87,7 +90,13 @@ private void AnalyzeOperation(OperationAnalysisContext context)
return;
}
var syntaxTree = context.Operation.Syntax.SyntaxTree;
var simpleName = memberReference.Syntax as TSimpleNameSyntax;
if (simpleName == null)
{
return;
}
var syntaxTree = simpleName.SyntaxTree;
var cancellationToken = context.CancellationToken;
var optionSet = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();
if (optionSet == null)
......@@ -95,12 +104,12 @@ private void AnalyzeOperation(OperationAnalysisContext context)
return;
}
var language = context.Operation.Syntax.Language;
var applicableOption = GetApplicableOptionFromSymbolKind(memberReference.Member.Kind);
var optionValue = optionSet.GetOption(applicableOption, language);
var applicableOption = QualifyMembersHelpers.GetApplicableOptionFromSymbolKind(memberReference.Member.Kind);
var optionValue = optionSet.GetOption(applicableOption, simpleName.Language);
var shouldOptionBePresent = optionValue.Value;
var isQualificationPresent = IsAlreadyQualifiedMemberAccess(memberReference.Instance.Syntax);
var isQualificationPresent = IsAlreadyQualifiedMemberAccess(instanceSyntaxOpt);
if (shouldOptionBePresent && !isQualificationPresent)
{
var severity = optionValue.Notification.Value;
......@@ -108,12 +117,15 @@ private void AnalyzeOperation(OperationAnalysisContext context)
{
context.ReportDiagnostic(Diagnostic.Create(
GetDescriptorWithSeverity(severity),
context.Operation.Syntax.GetLocation()));
simpleName.GetLocation()));
}
}
}
}
internal static PerLanguageOption<CodeStyleOption<bool>> GetApplicableOptionFromSymbolKind(SymbolKind symbolKind)
internal static class QualifyMembersHelpers
{
public static PerLanguageOption<CodeStyleOption<bool>> GetApplicableOptionFromSymbolKind(SymbolKind symbolKind)
{
switch (symbolKind)
{
......
......@@ -2,17 +2,18 @@
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.QualifyMemberAccess
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.QualifyMemberAccess
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend NotInheritable Class VisualBasicQualifyMemberAccessDiagnosticAnalyzer
Inherits AbstractQualifyMemberAccessDiagnosticAnalyzer(Of SyntaxKind)
Inherits AbstractQualifyMemberAccessDiagnosticAnalyzer(Of SyntaxKind, ExpressionSyntax, SimpleNameSyntax)
Protected Overrides Function GetLanguageName() As String
Return LanguageNames.VisualBasic
End Function
Protected Overrides Function IsAlreadyQualifiedMemberAccess(node As SyntaxNode) As Boolean
Protected Overrides Function IsAlreadyQualifiedMemberAccess(node As ExpressionSyntax) As Boolean
Return node.IsKind(SyntaxKind.MeExpression)
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册