Files
plugin-library/Assets/01.CodeChecker/Editor/Rules/CheckClass.cs
2025-03-04 16:02:44 +08:00

267 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using UnityEngine;
namespace wvdet.CodeChecker
{
/// <summary>
/// Class [类] 命名规范
/// 使用 Pascal Case。
/// 使用名词或者名词性词组命名。
/// 文件名应和类名相同。
/// 不要轻易使用缩写。
/// 不使用type前缀。
/// 比如C来标识Class。
/// 比如使用FileStream而不是CFileStream。
/// 不使用下划线。
/// 在合适的时候,使用单词复合来标识从某个基类继承而来。
/// 比如xxxException。
/// 同一功能下,可以考虑同一命名前缀。
/// </summary>
public class CheckClass : ICheckable
{
public List<Detail> Check(string filepath, CompilationUnitSyntax root)
{
var results = new List<Detail>();
var classDecs = root.DescendantNodes().OfType<ClassDeclarationSyntax>().ToList();
foreach (var dec in classDecs)
{
CheckClassName(dec, results);
CheckProperty(dec, results);
CheckField(dec, results);
CheckMethod(dec, results);
}
var filename = Path.GetFileNameWithoutExtension(filepath);
if (classDecs.Count > 0 && classDecs.All(x => x.Identifier.ValueText != filename))
{
results.Add(new Detail
{
level = Level.Warning,
guideline = "文件名应和类名相同",
});
}
return results;
}
private static void CheckClassName(ClassDeclarationSyntax dec, List<Detail> results)
{
var identifier = dec.Identifier.ValueText;
var lineNumber = dec.GetLocation().GetLineSpan().StartLinePosition.Line;
if (CheckerUtils.IsChineseIdentifier(identifier, lineNumber, out var ret))
results.Add(ret);
if (char.IsLower(identifier[0]))
{
results.Add(new Detail
{
level = Level.Warning,
line = lineNumber,
codeSnippet = identifier.Trim(),
suggestion = CheckerUtils.ConvertPascalCase(identifier),
guideline = "类名必须使用PascalCase",
});
}
if (identifier[0] == '_')
{
results.Add(new Detail
{
level = Level.Error,
line = lineNumber,
codeSnippet = identifier.Trim(),
suggestion = CheckerUtils.ConvertPascalCase(identifier.Substring(1)),
guideline = "类名禁用以下划线开始",
});
}
}
/// <summary>
/// Static Field [静态变量] 命名规范
/// 使用 Pascal Case。
/// 使用名词、名词性词组或者名词地缩写来命名静态变量。
/// 不要在静态变量名称中使用匈牙利命名法[变量名=属性+类型+对象描述]。
/// 在任何可能的情况下推荐你使用静态properties[属性]而不是public static fields。
/// </summary>
private static void CheckField(ClassDeclarationSyntax dec, List<Detail> results)
{
var fields = dec.DescendantNodes().OfType<FieldDeclarationSyntax>();
foreach (var field in fields)
{
var modifiers = field.Modifiers;
var isConst = modifiers.Any(x => x.IsKind(SyntaxKind.ConstKeyword));
var isPublic = modifiers.Any(x => x.IsKind(SyntaxKind.PublicKeyword));
var isStatic = modifiers.Any(x => x.IsKind(SyntaxKind.StaticKeyword));
var identifier = field.GetIdentifier().ValueText;
var lineNumber = field.GetLocation().GetLineSpan().StartLinePosition.Line;
if (CheckerUtils.IsChineseIdentifier(identifier, lineNumber, out var ret))
results.Add(ret);
if (isConst)
{
if (identifier.Any(char.IsLower))
results.Add(new Detail
{
level = Level.Error,
line = lineNumber,
codeSnippet = field.GetFieldGreenText(),
guideline = "Const Field[常量]全大写命名下划线分隔。例如SCENE_CAMERA。",
});
continue;
}
if (isPublic)
{
if (isStatic && char.IsLower(identifier[0]))
{
results.Add(new Detail
{
level = Level.Error,
line = lineNumber,
codeSnippet = field.GetFieldGreenText(),
guideline = "public静态变量首字母必须大写",
});
}
else if (!isStatic && char.IsUpper(identifier[0]))
{
results.Add(new Detail
{
level = Level.Error,
line = lineNumber,
codeSnippet = field.GetFieldGreenText(),
guideline = "public字段首字母必须小写",
});
}
}
else //私有
{
if (char.IsUpper(identifier[0]))
{
results.Add(new Detail
{
level = Level.Error,
line = lineNumber,
codeSnippet = field.GetFieldGreenText(),
guideline = "private及protected字段首字母必须小写",
});
}
var declarator = field.GetPredefined();
if (declarator != null)
{
if (declarator.Keyword.IsKind(SyntaxKind.BoolKeyword) &&
(identifier.Length < 3 || !identifier.StartsWith("is")))
{
results.Add(new Detail
{
level = Level.Warning,
line = lineNumber,
codeSnippet = field.GetFieldGreenText(),
guideline = "private及protected布尔字段应以is开始如isFileFound",
});
}
}
if (identifier.TrimStart('_').Contains('_'))
{
results.Add(new Detail
{
level = Level.Warning,
line = lineNumber,
codeSnippet = field.GetFieldGreenText(),
guideline = "private及protected中间不使用下划线(如isPlayer)",
});
}
}
}
}
private static void CheckMethod(ClassDeclarationSyntax dec, List<Detail> results)
{
var methods = dec.DescendantNodes().OfType<MethodDeclarationSyntax>();
foreach (var method in methods)
{
var identifier = method.Identifier.ValueText;
var lineNumber = method.GetLocation().GetLineSpan().StartLinePosition.Line;
if (CheckerUtils.IsChineseIdentifier(identifier, lineNumber, out var ret))
results.Add(ret);
if (!char.IsUpper(identifier[0]))
{
results.Add(new Detail
{
level = Level.Error,
line = lineNumber,
codeSnippet = identifier.Trim(),
suggestion = CheckerUtils.ConvertPascalCase(identifier),
guideline = "方法名必须使用PascalCase",
});
}
method.ParameterList.Parameters.ToList().ForEach(parameter =>
{
var parameterName = parameter.Identifier.ValueText;
if (CheckerUtils.IsChineseIdentifier(parameterName, lineNumber, out var ret2))
results.Add(ret2);
if (!char.IsLower(parameterName[0]))
{
results.Add(new Detail
{
level = Level.Error,
line = lineNumber,
codeSnippet = parameter.GetText().ToString().Trim(),
guideline = "方法参数名必须使用小写字母开始",
});
}
if (identifier.Contains('_'))
{
results.Add(new Detail
{
level = Level.Error,
line = lineNumber,
codeSnippet = parameter.GetText().ToString().Trim(),
guideline = "方法参数名禁止包含下划线(_)",
});
}
});
}
}
private static void CheckProperty(ClassDeclarationSyntax dec, List<Detail> results)
{
var properties = dec.DescendantNodes().OfType<PropertyDeclarationSyntax>();
foreach (var property in properties)
{
var identifier = property.Identifier.ValueText;
var lineNumber = property.GetLocation().GetLineSpan().StartLinePosition.Line;
if (CheckerUtils.IsChineseIdentifier(identifier, lineNumber, out var ret))
results.Add(ret);
if (!char.IsUpper(identifier[0]))
{
results.Add(new Detail
{
level = Level.Error,
line = lineNumber,
codeSnippet = property.GetText().ToString().Trim(),
suggestion = CheckerUtils.ConvertPascalCase(identifier),
guideline = "属性名须使用Camel Case命名",
});
}
}
}
}
}