如何编写C#/Roslyn分析器以匹配ReactiveUI属性?

如何编写C#/Roslyn分析器以匹配ReactiveUI属性?,c#,roslyn-code-analysis,C#,Roslyn Code Analysis,如果我有以下惯用的属性声明 private int _Foo; public int Foo { get { return _Foo; } set { this.RaiseAndSetIfChanged(ref _Foo, value); } } 其中Foo可以是whatshover的任何名称。作为roslyn分析仪的一部分,诊断匹配器是什么 我已经准备好编写我想要的代码修复,但目前我允许它应用于任何属性。我想收紧它,这样它只适用于与上述形状完全相同的属性 我目前的尝试是使用

如果我有以下惯用的属性声明

private int _Foo;
public int Foo { 
   get { return _Foo; } 
   set { this.RaiseAndSetIfChanged(ref _Foo, value); } 
}
其中Foo可以是whatshover的任何名称。作为roslyn分析仪的一部分,诊断匹配器是什么

我已经准备好编写我想要的代码修复,但目前我允许它应用于任何属性。我想收紧它,这样它只适用于与上述形状完全相同的属性

我目前的尝试是使用正则表达式剥离琐事,然后执行ToString,然后精确匹配字符串输出。我不确定是否有更好的方法

using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace ReactiveUI.Fody.CodeFix
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ReactiveUIFodyCodeFixAnalyzer : DiagnosticAnalyzer
{
    public const string DiagnosticId = "ReactiveUIFodyCodeFix";

    // You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat.
    // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Localizing%20Analyzers.md for more on localization
    private static readonly string Title = "ReactiveUI.Fody CodeFix";
    private static readonly string MessageFormat = "The property {0} can be simplified using ReactiveUI.Fody";

    private static readonly string Description =
    "This code fix changes boiler plate INPC property declaration to simple ones using a fody attribute";

    private const string Category = "Properties";

    private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

    public override void Initialize(AnalysisContext context)
    {
    context.RegisterSymbolAction(ChangePropertyModifierRule, SymbolKind.Property);
    }


    private static void ChangePropertyModifierRule(SymbolAnalysisContext context)
    {
    var propertySymbol = context.Symbol as IPropertySymbol;


    // Check if the ReactiveAttribute is already applied and if so 
    // skip it.
    var attributes = propertySymbol.GetAttributes();
    if (attributes.Any(attr => attr.AttributeClass.Name == "Reactive"))
        return;

    if (propertySymbol != null)
    {
        var property = propertySymbol.DeclaringSyntaxReferences[0].GetSyntax() as PropertyDeclarationSyntax;
        Debug.Assert(property != null, "property != null");
        var getter = property.AccessorList?.Accessors.FirstOrDefault(x => x.IsKind(SyntaxKind.GetAccessorDeclaration));
        var setter = property.AccessorList?.Accessors.FirstOrDefault(x => x.IsKind(SyntaxKind.SetAccessorDeclaration));

        if (setter?.Body == null)
        return;
        if (getter?.Body == null)
        return;

        if (setter.Body.Statements.Count != 1)
        return;

        var statement = setter.Body;
        if (!statement.ToString().Contains("RaiseAndSetIfChanged"))
        return;
        var idiom = $"{{this.RaiseAndSetIfChanged(ref_{propertySymbol.Name},value);}}";
        var noWhiteSpace = System.Text.RegularExpressions.Regex.Replace(statement.ToString(), @"\s+", "");

        if (idiom != noWhiteSpace)
        return;

        // Add "contextual" menu to change property modifiers.
        var diagnostic = Diagnostic.Create(Rule, propertySymbol.Locations[0], propertySymbol.Name);

        context.ReportDiagnostic(diagnostic);
    }
    }
}
}
使用System.Linq;
使用系统线程;
使用Microsoft.CodeAnalysis;
使用Microsoft.CodeAnalysis.CSharp;
使用Microsoft.CodeAnalysis.CSharp.Syntax;
使用Microsoft.CodeAnalysis.Diagnostics;
命名空间ReactiveUI.Fody.CodeFix
{
[诊断分析仪(LanguageNames.CSharp)]
公共类ReactiveUIFodyCodeFixAnalyzer:DiagnosticanAnalyzer
{
public const string DiagnosticId=“ReactiveUIFodyCodeFix”;
//您可以在Resources.resx文件中更改这些字符串。如果您不希望您的分析器能够本地化,可以使用常规字符串作为标题和消息格式。
//看https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Localizing%20Analyzers.md 有关本地化的更多信息
私有静态只读字符串Title=“ReactiveUI.Fody CodeFix”;
私有静态只读字符串MessageFormat=“可以使用ReactiveUI.Fody简化属性{0}”;
私有静态只读字符串描述=
“此代码修复将锅炉板INPC属性声明更改为使用fody属性的简单声明”;
私有常量字符串Category=“Properties”;
私有静态只读DiagnosticDescriptor规则=新的DiagnosticDescriptor(DiagnosticId、标题、消息格式、类别、DiagnosticSeverity.Warning、isEnabledByDefault:true、描述:description);
公共覆盖ImmutableArray SupportedDiagnostics=>ImmutableArray.Create(规则);
公共覆盖无效初始化(AnalysisContext上下文)
{
RegisterSymbolAction(ChangePropertyModifierRule,SymbolKind.Property);
}
私有静态void ChangePropertyModifierRule(SymbolAnalysisContext上下文)
{
var propertySymbol=上下文。符号为IPropertySymbol;
//检查是否已应用ReactiveAttribute,如果已应用
//跳过它。
var attributes=propertySymbol.GetAttributes();
if(attributes.Any(attr=>attr.AttributeClass.Name==“被动”))
返回;
if(propertySymbol!=null)
{
var property=propertySymbol.DeclaringSyntaxReferences[0].GetSyntax()作为PropertySclarationSyntax;
Assert(property!=null,“property!=null”);
var getter=property.AccessorList?.Accessors.FirstOrDefault(x=>x.IsKind(SyntaxKind.GetAccessorDeclaration));
var setter=property.AccessorList?.Accessors.FirstOrDefault(x=>x.IsKind(SyntaxKind.SetAccessorDeclaration));
if(setter?.Body==null)
返回;
if(getter?.Body==null)
返回;
if(setter.Body.Statements.Count!=1)
返回;
var语句=setter.Body;
如果(!statement.ToString()包含(“RaiseAndSetIfChanged”))
返回;
var-idiom=$“{{this.RaiseAndSetIfChanged(ref{propertySymbol.Name},value);}}”;
var noWhiteSpace=System.Text.RegularExpressions.Regex.Replace(statement.ToString(),@“\s+”,”);
if(成语!=noWhiteSpace)
返回;
//添加“上下文”菜单以更改属性修改器。
var diagnostic=diagnostic.Create(规则,propertySymbol.Locations[0],propertySymbol.Name);
上下文。报告诊断(诊断);
}
}
}
}

是否也应识别此项:
RaiseAndSetIfChanged(参考此值)?如果属性
X
的基础字段被称为
X
(甚至是
\u y
),该怎么办?是的,但idomatic方式是from。我在ReactiveUI上有一个打开拉取请求。Fody在。如果您愿意帮助使用更灵活的分析仪,我们将不胜感激。问题是:您现在拥有的功能正在发挥作用。你在问我们一个更好的方法,但你必须告诉我们这意味着什么。如何更好?产生完全相同的匹配,但以更有效的方式?或者在匹配的方面做得更好(因此也不同)?给我们举个例子,问一个具体的问题。我问这个问题时没有答案。但是现在,我想还是不要在整个语句中使用字符串匹配更好。因此,准确地说,“更好”的方法是通过某种方法在语法树上执行模式匹配。但我可能又错了,这可能是最好的方式做这样的事情。这是我的第一个分析器,在写这个问题的时候,我还不知道如何得到任何解决方案。请注意,我使用正则表达式折叠所有空白(删除所有琐事),然后将其与预期值进行比较。我希望看到等效的代码,它将语法树中的所有琐事剥离出来,然后与预期的语法树进行比较,而不将整个语法树折叠成字符串。是否也可以识别这一点:
RaiseAndSetIfChanged(请参阅此值)。\u Foo?如果属性
X
的基础字段被称为
X
(甚至是
\u y
),该怎么办?是的,但idomatic方式是from。我在ReactiveUI上有一个打开拉取请求。Fody在。如果您愿意帮助使用更灵活的分析仪,我们将不胜感激。问题是:您现在拥有的功能正在发挥作用。你在问我们一个更好的方法,但你必须告诉我们这意味着什么。如何更好?产生完全相同的匹配,但以更有效的方式?或者在匹配的方面做得更好(因此也不同)?给我们举个例子,问一个具体的问题。我问这个问题时没有答案。但现在我知道了,我想还是会更好