使用Roslyn CodeFixProvider为方法添加访问修饰符?

17

几天前我参加了TechEd,看到了Kevin Pilch-Bisson的这个演讲(相关部分从大约18分钟开始)......我觉得它很酷,所以决定自己试着使用Roslyn。

我正在尝试制定一个“必须声明访问修饰符”(Stylecop SA1400)的规则-意思是,

以下内容违反了该规则:

    static void Main(string[] args)
    {
    }

这样就可以了:

    public static void Main(string[] args)
    {
    }

它必须有明确的internal、public、private或protected关键字。

检测到违规行为相当容易,但现在我正在尝试提供解决方法。我一直在尝试各种方法并到处搜索,但我找不到如何添加访问修饰符的方法。

这是我目前的代码:

public async Task<IEnumerable<CodeAction>> GetFixesAsync(Document document, TextSpan span, IEnumerable<Diagnostic> diagnostics, CancellationToken cancellationToken)
{
    var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
    var token = root.FindToken(span.Start);

    var methodDeclaration = token.Parent as MethodDeclarationSyntax;

    //var newModifiers = methodDeclaration.Modifiers.Add(SyntaxFactory.AccessorDeclaration(SyntaxKind.PublicKeyword));         
    //var newModifiers = new SyntaxTokenList() { new SyntaxToken() };

    MethodDeclarationSyntax newMethodDeclaration = methodDeclaration.WithModifiers(methodDeclaration.Modifiers);
    var newRoot = root.ReplaceNode(methodDeclaration, newMethodDeclaration);
    var newDocument = document.WithSyntaxRoot(newRoot);

    return new[] { CodeAction.Create("Add Public Keyword", newDocument) };
}
WithModifiers 需要一个 SyntaxTokenList,我可以使用 New() 方法来创建它,但是我不知道如何将其制作为 SyntaxKind.PublicKeyword。我也不确定是否应该使用 SyntaxFactory 而不是 new 它。然而,当使用 SyntaxFactory 时,我也无法确定需要使用哪种方法来创建一个 SyntaxKind.PublicKeywordSyntaxToken。如果有兴趣,我可以发布整个内容,包括 DiagnosticAnalyzer
3个回答

12
很高兴您喜欢这次的演讲!实际上,我们在语法模型中有一些辅助工具,使得添加项目到列表变得更加容易,所以您应该能够像这样做:

很高兴您喜欢这次的演讲!实际上,我们在语法模型中有一些辅助工具,使得添加项目到列表变得更加容易,所以您应该能够像这样做:

var newMethodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword));

获取新方法声明。

这个的完整形式可能是这样的:

var newModifiers = SyntaxFactory.TokenList(modifiers.Concat(new[] { SyntaxFactory.Token(SyntaxKind.PublicKeyword)}));
var newMethodDeclaration = methodDeclaration.WithModifiers(newModifiers);

希望这能帮到你。


我非常喜欢这个演讲!这是我在teched上看过的最好的演讲之一,与Async演讲并列 :) - 无论如何,你的建议有点可行,但最终我得到的是 static public void Main。我已经添加了自己的解决方案,似乎可以工作。如果可能的话,请查看一下我的解决方案,以防我做了什么奇怪的事情,那就太好了。这是我为Roslyn制作的第一件事,所以可能有很多需要改进的地方。 - Ron Sijm
顺便问一下,为什么AnalyzeNode的语法是这样的:public void AnalyzeNode(SyntaxNode node, SemanticModel semanticModel, Action<Diagnostic> addDiagnostic而不是这样的:public Action<Diagnostic> AnalyzeNode(SyntaxNode node, SemanticModel semanticModel?看起来有点奇怪。 - Ron Sijm
1
因为您可以报告多个诊断结果。此外,将它们作为回调函数让我们避免了一些分配问题,并且可以在异步报告结果时流式传输它们。 - Kevin Pilch

4
我实际需要的是这个:

var newModifiers = SyntaxFactory.TokenList(SyntaxFactory.Token(accessModifierToken))
    .AddRange(methodDeclaration.Modifiers);

这几乎是Chris Eelmaa建议的内容,但是我用那个建议最终得到了static public void Main,虽然它是有效的,但很丑陋。

添加public将其添加到列表末尾,据我所知,访问修饰符应始终放在第一位。


1
看起来不错,除了你有一个未使用的本地变量“public TokenList”。你的Func将被调用来创建预览以及在单击时,但这应该没问题。 - Kevin Pilch
好的,谢谢 :)。是的,我忘记了publicTokenList,通常resharper会提醒我,但由于我在vs14中工作,它没有那个功能。 - Ron Sijm

1

如果想要创建指示“public static”的修饰符,您可以使用以下代码:

var modifiers = SyntaxFactory.TokenList(
    new SyntaxToken[] { 
        SyntaxFactory.Token(SyntaxKind.PublicKeyword), 
        SyntaxFactory.Token(SyntaxKind.StaticKeyword) 
    }),

但是就您的情况而言,我不明白为什么。
var updatedModifiers = methodDeclaration
                .Modifiers
                .Add(SyntaxFactory.Token(SyntaxKind.PublicKeyword));

methodDeclaration.WithModifiers(updatedModifiers);

不起作用。

第一个建议不可行,因为显然并不总是需要静态的,但我明白这只是为了解释语法... 然而,第二个建议也不太可行,它会导致 static public void Main 或者 static publicvoid Main - Ron Sijm
@RonSijm,+1。如果您整理一下答案并只保留相关内容(例如去掉GetFixesAsyncCreateAction),并接受它,我没有任何问题。这是一个好问题。 - Erti-Chris Eelmaa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接