コードレビューをする際に、レビュー項目にコーディング規約に則した記述をしているかどうかという物がある。
チェック自体は行った方が保守性が高まり良いとは思うが、人の手で行うとかなり煩わしく、量もあるので時間もかかる。
ルールがあり確認作業も単調なので、どうにか機械的に行う事は出来ないかと調べていたら、C#にはRoslynという構文解析に使えるAPIが備わっているようだったので、試してみた。
今回は、使い方メモ書き、プロパティ編。
ハッキリ言って、Roslynがどういう構造で、どういう思想なのかをきちんと理解はしておらず、
とりあえずやりたい事を、こういう風にやった、というメモ書きである。
これから使い始める人の、調査のヒントとして使ってもらえれば良いと思う。
1. まず、解析したいソースコードを、文字列として取得
方法は何でもいいけど、とりあえず文字列を生成
[MenuItem("ShortCutCommand/Syntax4")] private static void Syntax4() { var fileName = "Resources/GameScene.txt"; StreamReader sr = new StreamReader(@"./Assets/Resources/GameScene.txt", Encoding.GetEncoding("UTF-8")); string code = sr.ReadToEnd();// ソースコード取得 sr.Close();
2. クラスの情報を取る為の準備
[MenuItem("ShortCutCommand/Syntax4")] private static void Syntax4() { var fileName = "Resources/GameScene.txt"; StreamReader sr = new StreamReader(@"./Assets/Resources/GameScene.txt", Encoding.GetEncoding("UTF-8")); string code = sr.ReadToEnd();// ソースコード取得 sr.Close(); // これで、とりあえず構文解析データのRootを取ってきているっぽい SyntaxTree tree = CSharpSyntaxTree.ParseText(code); var root = tree.GetCompilationUnitRoot(); // ネームスペース(次の階層まとまり)を取得 MemberDeclarationSyntax firstMember = root.Members[0]; var nameSpaceDeclaration = (NamespaceDeclarationSyntax)firstMember; // ネームスペース内の最初のクラス情報を取得 var classDeclaration = (ClassDeclarationSyntax)nameSpaceDeclaration.Members[0];
3. クラスのプロパティの情報を取得
[MenuItem("ShortCutCommand/Syntax4")] private static void Syntax4() { var fileName = "Resources/GameScene.txt"; StreamReader sr = new StreamReader(@"./Assets/Resources/GameScene.txt", Encoding.GetEncoding("UTF-8")); string code = sr.ReadToEnd();// ソースコード取得 sr.Close(); // これで、とりあえず構文解析データのRootを取ってきているっぽい SyntaxTree tree = CSharpSyntaxTree.ParseText(code); var root = tree.GetCompilationUnitRoot(); // ネームスペース(次の階層まとまり)を取得 MemberDeclarationSyntax firstMember = root.Members[0]; var nameSpaceDeclaration = (NamespaceDeclarationSyntax)firstMember; // ネームスペース内の最初のクラス情報を取得 var classDeclaration = (ClassDeclarationSyntax)nameSpaceDeclaration.Members[0]; // プロパティ情報の取得 var propertyList = classDeclaration.DescendantNodes() .OfType<PropertyDeclarationSyntax>().ToList();
4. 各情報取得
// プロパティ情報の取得 var propertyList = classDeclaration.DescendantNodes() .OfType<PropertyDeclarationSyntax>().ToList(); for (int i = 0; i < propertyList.Count; i++) { // 1 戻り値の型 string returnType = propertyList[i].Type.ToString(); // 2 プロパティ名 string propertyName = propertyList[i].Identifier.ValueText; // 3 private,public,protected/async,virtual等の修飾子 var modifierTexts = propertyList[i].Modifiers.Select(x => x.Text).ToArray(); // 4 コメント調べたい if (propertyList[i].HasLeadingTrivia == true) { var triviaList = propertyList[i].GetLeadingTrivia(); var commentSyntaxTriviaArray = triviaList.Where(trivia => (!trivia.IsKind(SyntaxKind.WhitespaceTrivia)) && (!trivia.IsKind(SyntaxKind.EndOfLineTrivia))).ToArray(); if (commentSyntaxTriviaArray.Length == 0) { // コメントが無い } else { // コメントが行数分含まれる for (int i2 = 0; i2 < commentSyntaxTriviaArray.Length; i2++) { commentSyntaxTriviaArray[i2].ToString(); } } } else { // コメントが無い } }