本文为大家带来 Laugh 的“主程必杀技”系列文章第二篇《Unity Analyzer 自动化执行代码规范 -强制加注释(类注释/方法注释)》,系列其他文章将每周更新,或可访问 Laugh 的 Unity 官方开发者社区主页,持续关注学习。
演示代码
获取链接:https://gitee.com/palang/unity-sharp-code-regulator.git
⦁ 检查继承关系
Unity Analyzer 自动化执行代码规范
强制加注释
注释的重要性,主程们应该够能感同深受,这里不再啰嗦。但有多少项目拥有完整的注释,很多人也都心知肚明。今天举个小的例子,来说明如何给类和方法强制添加 xml 文档注释,个性化的需求可以自己添加。
一、环境准备
4. Microsoft.CodeAnalysis.CSharp,安装 3.8.0 版本 基本过程可以参考上一篇文章。
二、实现目标
1. 强制所有类都加 xml 文档注释
2. 强制所有 public 方法都加 xml 文档注释
/// <summary>
/// 这是一个测试类,主要用于强制注释的测试[
/// </summary>
public class TestClass](
/// </summary>
public class TestClass)
{
/// <summary>
/// 这是一个测试方法,用户测试方法注释
/// </summary>
public void Test()
{
}
}
三、编写 Analyzer 分析器
首先定义错误 ID 在类 DianogsticIDs 中添加成员
public const string FORCE_Comments_ID = "FERR1002";
using System;
using System.Collections.Generic;
using System.Text;
namespace Analyzer
{
public static class DianogsticIDs
{
public const string FORCE_BRACE_ID = "FERR1001";
public const string FORCE_Comments_ID = "FERR1002";
}
}
然后在 AnalyzerReleases.Unshipped.md 文件添加该规则,如下:
; Unshipped analyzer release
; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
### New Rules
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
FERR1001 | Criterion | Error | ForceStatementBrace
FERR1002 | Criterion | Error | ForceClassComments
1. 类注释
using Analyzer;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
/**
* Author: Laugh(笑微)
* https://developer.unity.cn/projects/65937455edbc2a001cbd8102
*/
namespace CdeAnalyzer
{
[ ]
internal class ForceClassComments : DiagnosticAnalyzer
{
/// <summary>
/// 错误描述
/// </summary>
private static readonly DiagnosticDescriptor ForceCommentsDescriptor =
new DiagnosticDescriptor(
DianogsticIDs.FORCE_Comments_ID, // ID
"类必须添加XML文档注释", // Title
"类必须添加XML文档注释", // Message format
DiagnosticCategories.Criterion, // Category
DiagnosticSeverity.Error, // Severity
isEnabledByDefault: true // Enabled by default
);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(ForceCommentsDescriptor);
/// <summary>
/// 初始化分析方法
/// </summary>
/// <param name="context"></param>
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxTreeAction(AnalyzeSymbol);
}
/// <summary>
/// 检测方法
/// </summary>
/// <param name="context"></param>
private static void AnalyzeSymbol(SyntaxTreeAnalysisContext context)
{
//找到文档的语法根树
var root = context.Tree.GetRoot(context.CancellationToken);
//找到所有类定义
var classNodeList = root.DescendantNodes()?.OfType<ClassDeclarationSyntax>();
foreach(var cls in classNodeList)
{
var classDeclaration = (ClassDeclarationSyntax)cls;
var tokens = cls.ChildTokens();
foreach (var token in tokens)
{
//要列举多种关键字:public, internal, protected, private, no keyword
if(token.IsKind(SyntaxKind.PublicKeyword)
|| token.IsKind(SyntaxKind.InternalKeyword)
|| token.IsKind(SyntaxKind.ClassKeyword)
|| token.IsKind(SyntaxKind.ProtectedKeyword)
|| token.IsKind(SyntaxKind.PrivateKeyword))
{
//获取注释文本
var comments = token.GetAllTrivia().ToSyntaxTriviaList().ToString();
//获取有效注释行
List<SyntaxTrivia> liables = new List<SyntaxTrivia>();
var triviaList = token.GetAllTrivia();
foreach (var trivia in triviaList)
{
if(trivia.IsKind(SyntaxKind.SingleLineCommentTrivia))
{
if(!trivia.ToString().Contains("<summary>") && !trivia.ToString().Contains("</summary>"))
{
liables.Add(trivia);
}
}
}
var liablesStr = string.Join("",liables);
liablesStr = liablesStr.Replace("///", "").Trim();
if(liablesStr.Length < 4)/*注释长度过短,可以自己添加其他规则*/
{
//报错
var diagnostic = Diagnostic.Create(ForceCommentsDescriptor, cls.GetFirstToken().GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
//只判断第一个符合条件的
break;
}
}
}
}
}
2. 方法注释
新建分析器类 ForceFunctionComments.cs,内容如下(具体看代码中的注释)
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis;
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.Immutable;
using Analyzer;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp;
using System.Linq;
namespace CdeAnalyzer
{
[ ]
internal class FroceFunctionComments : DiagnosticAnalyzer
{
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(ForceCommentsDescriptor);
/// <summary>
/// 错误描述
/// </summary>
private static readonly DiagnosticDescriptor ForceCommentsDescriptor =
new DiagnosticDescriptor(
DianogsticIDs.FORCE_Comments_ID, // ID
"类必须添加XML文档注释", // Title
"类必须添加XML文档注释", // Message format
DiagnosticCategories.Criterion, // Category
DiagnosticSeverity.Error, // Severity
isEnabledByDefault: true // Enabled by default
);
/// <summary>
/// 初始化分析方法
/// </summary>
/// <param name="context"></param>
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxTreeAction(AnalyzeSymbol);
}
/// <summary>
/// 检测方法
/// </summary>
/// <param name="context"></param>
private static void AnalyzeSymbol(SyntaxTreeAnalysisContext context)
{
//找到文档的语法根树
var root = context.Tree.GetRoot(context.CancellationToken);
//找到所有类定义
var methodNodeList = root.DescendantNodes()?.OfType<MethodDeclarationSyntax>();
foreach (var meth in methodNodeList)
{
var classDeclaration = (MethodDeclarationSyntax)meth;
var tokens = meth.ChildTokens();
foreach (var token in tokens)
{
//关键字:public,其他关键字可以自行处理:internal, protected, private, no keyword
if (token.IsKind(SyntaxKind.PublicKeyword))
{
//获取注释文本
var comments = token.GetAllTrivia().ToSyntaxTriviaList().ToString();
//获取有效注释行
List<SyntaxTrivia> liables = new List<SyntaxTrivia>();
var triviaList = token.GetAllTrivia();
foreach (var trivia in triviaList)
{
if (trivia.IsKind(SyntaxKind.SingleLineCommentTrivia))
{
if (!trivia.ToString().Contains("<summary>") && !trivia.ToString().Contains("</summary>"))
{
liables.Add(trivia);
}
}
}
var liablesStr = string.Join("", liables);
liablesStr = liablesStr.Replace("///", "").Trim();
if (liablesStr.Length < 4)/*注释长度过短,可以自己添加其他规则*/
{
//报错
var diagnostic = Diagnostic.Create(ForceCommentsDescriptor, meth.GetFirstToken().GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
//只判断第一个符合条件的
break;
}
}
}
}
}
代码可以直接使用。