コードレビューをする際に、レビュー項目にコーディング規約に則した記述をしているかどうかという物がある。
チェック自体は行った方が保守性が高まり良いとは思うが、人の手で行うとかなり煩わしく、量もあるので時間もかかる。
ルールがあり確認作業も単調なので、どうにか機械的に行う事は出来ないかと調べていたら、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 methodList = classDeclaration.DescendantNodes() .OfType<MethodDeclarationSyntax>().ToList();
4. 各情報取得
// メソッド情報の取得 var methodList = classDeclaration.DescendantNodes() .OfType<MethodDeclarationSyntax>().ToList(); // 関数部分のチェック for (int i = 0; i < methodList.Count; i++) { // 1 戻り値の型 string returnType = methodList[i].ReturnType.ToString(); // 2 関数名 string methodName = methodList[i].Identifier.ValueText; // 3 private,public,protected/async,virtualの修飾子名 var modifierTexts = methodList[i].Modifiers.Select(x => x.Text).ToArray(); // 4 引数調べたい var parameterTexts = methodList[i].ParameterList.Parameters.Select(x => x).ToArray(); for (int i2 = 0; i2 < parameterTexts.Length; i2++) { string name = parameterTexts[i2].Identifier.ValueText;// test string fullName = parameterTexts[i2].ToString();// int test } // 5 コメント調べたい if (methodList[i].HasLeadingTrivia == true) { var triviaList = methodList[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 { // コメント無し } }