演示代码
Analyzer 初体验
强制使用大括号包括逻辑分支 (if...else...)
int a = 0;
if(a > 0)
a ++;
else
a --;
int a = 0;
if(a > 0)
{
a ++;
}
else
{
a --;
}
一、开发 Analyzer
工具使用 Visual Studio 2022 Community,搜索下载安装即可。
⦁ 使用 VS 新建类库项目
⦁ 类库框架选择 .Net Standard 2.0 (兼容 Unity)
⦁ 添加 Roslyn 包:搜索找到 Microsoft.CodeAnalysis.CSharp,安装 3.8.0 版本。安装成功后,依赖项中会出现“分类器”。
⦁创建 AnalyzerReleases.Unshipped.md 文件,以便后面添加 analyzer release ID。
using System.Collections.Generic;
using System.Text;
namespace CdeAnalyzer
{
public static class DianogsticIDs
{
public const string FORCE_BRACE_ID = "F-ERR1001";
}
public class DiagnosticCategories
{
public const string Criterion = "Criterion";
}
}
⦁ 排除目录定义(配置)ConstraintDefinition.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace Analyzer
{
public class ConstraintDefinition
{
public static List<string> AnalyzerExcludePath = new List<string>() {
"/PackageCache/",
"/ThirdLibs/",
"/Plugins/"
};
public static bool ExcludeAnalize(string path)
{
foreach(var file in AnalyzerExcludePath)
{
if(path.Contains(file))
{
return true;
}
}
return false;
}
}
}
⦁ 修改 AnalyzerReleases.Unshipped.md 文件如下
⦁ 新建类文件 ForceStatementBrace.cs,代码如下
using Microsoft.CodeAnalysis;using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
/**
* Author: Laugh(笑微)
* https://developer.unity.cn/projects/65937455edbc2a001cbd8102
*/
namespace Analyzer
{
[ ]
internal class ForceStatementBrace : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor FroceBraceDescriptor =
new DiagnosticDescriptor(
DianogsticIDs.FORCE_BRACE_ID, // ID
"逻辑分支必须使用花括号包括", // Title
"逻辑分支必须使用花括号包括", // Message format
DiagnosticCategories.Criterion, // Category
DiagnosticSeverity.Error, // Severity
isEnabledByDefault: true // Enabled by default
);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(FroceBraceDescriptor);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxTreeAction(AnalyzeSymbol);
}
private static void AnalyzeSymbol(SyntaxTreeAnalysisContext context)
{
var root = context.Tree.GetRoot(context.CancellationToken);
List<ExpressionStatementSyntax> reportedNode = new List<ExpressionStatementSyntax>();
foreach (var ifstatement in root.DescendantNodes()?.OfType<IfStatementSyntax>())
{
var expressStatements = ifstatement.DescendantNodes()?.OfType<ExpressionStatementSyntax>().ToList() ;
foreach(var estate in expressStatements)
{
if (ConstraintDefinition.ExcludeAnalize(context.Tree.FilePath))
{//排除特殊目录
return;
}
if (reportedNode.Contains(estate))
{
continue;
}
if(!(estate.Parent is BlockSyntax))
{
reportedNode.Add(estate);
var diagnostic = Diagnostic.Create(FroceBraceDescriptor, estate.GetFirstToken().GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
}
}
}
}
二、在 Unity 中使用
⦁ 新建 Unity 项目
⦁ 在 Assets 下新建 Scripts 目录
⦁ 把之前生成的 dll 文件放入 Assets/Scripts 目录下
⦁ 在 Unity project 出口中找到该 dll,点击选中,在 Inspector 设置如图:
⦁ 新建脚本 TestAnalyzer.cs,并添加测试脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestAnalyzer : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
int a = 0;
if (a == 0)
a = 1;
else
a = 2;
}
// Update is called once per frame
void Update()
{
}
}