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