SonarAnalyzer.CSharp でサイクロマティック複雑度を算出
C# ソースファイルのサイクロマティック複雑度(循環的複雑度)を算出するサンプルを SonarC# (SonarAnalyzer.CSharp) の API を利用して作ってみました。
今回、使用した環境は以下の通りです。
- SonarC# 7.13
- .NET Core SDK 3.0 preview3
ソースは http://github.com/fits/try_samples/tree/master/blog/20190422/
準備
dotnet コマンドを使ってプロジェクトを作成します。
プロジェクトの作成
> dotnet new console
C# のソースを構文解析する必要があるので Microsoft.CodeAnalysis.CSharp
パッケージを追加します。
Microsoft.CodeAnalysis.CSharp の追加
> dotnet add package Microsoft.CodeAnalysis.CSharp
次に、SonarAnalyzer.CSharp
パッケージを追加しますが、これは IDE(VisualStudio)用パッケージのようなので、単に add package してもプロジェクトで参照できるようにはなりません。(analyzers ディレクトリへ .dll が配置されているため)
そこで、以下のように指定のディレクトリへパッケージを配置し ※、.csproj
を編集する事で対応してみました。
※ 普通に add package して .nuget/packages ディレクトリへ 配置された dll のパスを設定する方法も考えられる
SonarAnalyzer.CSharp の追加(pkg ディレクトリへ配置)
> dotnet add package SonarAnalyzer.CSharp --package-directory pkg
上記コマンドで追加された PackageReference
要素をコメントアウトし、代わりに Reference
要素を追加します。(HintPath
で SonarAnalyzer.CSharp.dll のパスを指定)
sonar_sample.csproj の編集
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp3.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.0.0" /> <!-- 以下をコメントアウト <PackageReference Include="SonarAnalyzer.CSharp" Version="7.13.0.8313"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> --> <!-- 以下を追加 --> <Reference Include="SonarAnalyzer.CSharp"> <HintPath>./pkg/sonaranalyzer.csharp/7.13.0.8313/analyzers/SonarAnalyzer.CSharp.dll</HintPath> </Reference> </ItemGroup> </Project>
実装
C# のソースコードをパースして MethodDeclarationSyntax
を取り出し、CSharpCyclomaticComplexityMetric.GetComplexity
メソッドへ渡す事でサイクロマティック複雑度を算出します。
Program.cs
using System; using System.Linq; using System.IO; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using SonarAnalyzer.Metrics.CSharp; namespace CyclomaticComplexity { class Program { static void Main(string[] args) { using (var reader = new StreamReader(args[0])) { // ソースコードのパース var tree = CSharpSyntaxTree.ParseText(reader.ReadToEnd()); var root = tree.GetCompilationUnitRoot(); // MethodDeclarationSyntax の取得 var methods = root.DescendantNodes() .OfType<MethodDeclarationSyntax>(); foreach(var m in methods) { var c = CSharpCyclomaticComplexityMetric.GetComplexity(m); Console.WriteLine("{0},{1}", m.Identifier, c.Complexity); } } } } }
実行
ビルドして実行してみます。
ビルド
> dotnet build ・・・ ビルドに成功しました。 0 個の警告 0 エラー
Program.cs の複雑度を算出してみます。
実行1
> dotnet run Program.cs Main,2
SonarC# のソースで試してみます。
実行2
> cd .. > git clone https://github.com/SonarSource/sonar-dotnet.git ・・・ > cd sonar_sample > dotnet run ../sonar-dotnet/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/Metrics/CSharpMetrics.cs GetCognitiveComplexity,1 GetCyclomaticComplexity,1 IsClass,4 IsCommentTrivia,1 IsDocumentationCommentTrivia,4 IsEndOfFile,1 IsFunction,16 IsNoneToken,1 IsStatement,28