├── dependencies.json ├── logo.png ├── docs ├── info.png ├── options.png ├── warning.png └── ThresholdGuidance.md ├── tools ├── 7za.exe ├── nuget.exe └── vswhere.exe ├── global.json ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── src ├── rider │ └── main │ │ ├── resources │ │ ├── messages │ │ │ └── CyclomaticComplexityBundle.properties │ │ └── META-INF │ │ │ └── plugin.xml │ │ └── kotlin │ │ └── com │ │ └── jetbrains │ │ └── rider │ │ └── plugins │ │ └── cyclomaticcomplexity │ │ ├── options │ │ └── CyclomaticComplexityOptionsPage.kt │ │ └── CyclomaticComplexityBundle.kt └── dotnet │ ├── PowerToys.CyclomaticComplexity │ ├── ComplexityError.png │ ├── ComplexityWarning.png │ ├── ZoneMarker.cs │ ├── CyclomaticComplexity.dotSettings │ ├── IComplexityHighlighting.cs │ ├── Resources │ │ └── DefaultSettings.xml │ ├── CyclomaticComplexityAnalysisSettings.cs │ ├── Options │ │ ├── ComplexityAnalysisOptionsViewModel.cs │ │ ├── LanguageSpecificComplexityProperty.cs │ │ ├── HyperlinkOptionViewModel.cs │ │ └── ComplexityAnalysisOptionPage.cs │ ├── ComplexityAnalysisInvalidateOnThresholdChange.cs │ ├── PowerToys.CyclomaticComplexity.Rider.csproj │ ├── DefaultCyclomaticComplexitySettings.cs │ ├── PowerToys.CyclomaticComplexity.csproj │ ├── ComplexityWarningHighlight.cs │ ├── ComplexityInfoHighlight.cs │ ├── CyclomaticComplexityIcons │ │ ├── ThemedIcons.CyclomaticComplexity.Generated.Xaml │ │ └── CyclomaticComplexity.xaml │ └── ComplexityAnalysisElementProblemAnalyzer.cs │ ├── PowerToys.CyclomaticComplexity.Tests │ ├── TestEnvironment.cs │ ├── test │ │ └── data │ │ │ ├── CSharp │ │ │ ├── WorkaroundBoolAsConditional.cs │ │ │ ├── WorkaroundBoolAsConditional.cs.gold │ │ │ ├── ComplexMethodWithDefaultSettings.cs │ │ │ ├── ComplexMethodWithModifiedSettings.cs │ │ │ ├── ComplexMethodWithDefaultSettings.cs.gold │ │ │ ├── MethodBodyTooComplexForValueAnalysisButLowCyclomaticComplexity.cs │ │ │ ├── MethodBodyTooComplexForValueAnalysisButLowCyclomaticComplexity.cs.gold │ │ │ ├── MethodBodyTooComplexForValueAnalysisAndHighCyclomaticComplexity.cs │ │ │ ├── MethodBodyTooComplexForValueAnalysisAndHighCyclomaticComplexity.cs.gold │ │ │ └── ComplexMethodWithModifiedSettings.cs.gold │ │ │ ├── nuget.config │ │ │ ├── JS │ │ │ ├── AnonymousMethodWithDefaultSettings.js │ │ │ ├── AnonymousMethodWithModifiedSettings.js │ │ │ ├── NamedMethodWithDefaultSettings.js │ │ │ ├── NamedMethodWithModifiedSettings.js │ │ │ ├── AssignedFunctionWithDefaultSettings.js │ │ │ ├── AssignedFunctionWithModifiedSettings.js │ │ │ ├── NestedFunctionWithDefaultSettings.js │ │ │ ├── NestedFunctionWithModifiedSettings.js │ │ │ ├── AnonymousMethodWithDefaultSettings.js.gold │ │ │ ├── AssignedFunctionWithDefaultSettings.js.gold │ │ │ ├── NamedMethodWithDefaultSettings.js.gold │ │ │ ├── NestedFunctionWithDefaultSettings.js.gold │ │ │ ├── AnonymousMethodWithModifiedSettings.js.gold │ │ │ ├── AssignedFunctionWithModifiedSettings.js.gold │ │ │ ├── NamedMethodWithModifiedSettings.js.gold │ │ │ └── NestedFunctionWithModifiedSettings.js.gold │ │ │ ├── TypeScript │ │ │ ├── AnonymousMethodWithDefaultSettings.ts │ │ │ ├── AnonymousMethodWithModifiedSettings.ts │ │ │ ├── ModuleMethodWithDefaultSettings.ts │ │ │ ├── ModuleMethodWithModifiedSettings.ts │ │ │ ├── ClassMethodWithDefaultSettings.ts │ │ │ ├── ClassMethodWithModifiedSettings.ts │ │ │ ├── AnonymousMethodWithDefaultSettings.ts.gold │ │ │ ├── ModuleMethodWithDefaultSettings.ts.gold │ │ │ ├── ClassMethodWithDefaultSettings.ts.gold │ │ │ ├── AnonymousMethodWithModifiedSettings.ts.gold │ │ │ ├── ModuleMethodWithModifiedSettings.ts.gold │ │ │ └── ClassMethodWithModifiedSettings.ts.gold │ │ │ └── Cpp │ │ │ ├── MethodWithDefaultSettings.cpp │ │ │ ├── MethodWithModifiedSettings.cpp │ │ │ ├── MethodWithDefaultSettings.cpp.gold │ │ │ └── MethodWithModifiedSettings.cpp.gold │ ├── PowerToys.CyclomaticComplexity.Tests.csproj │ ├── TypeScriptComplexityHighlightingTest.cs │ ├── CppComplexityHighlightingTest.cs │ ├── CSharpComplexityHighlightingTest.cs │ └── JsComplexityHighlightingTest.cs │ ├── Plugin.props │ └── Directory.Build.props ├── .gitattributes ├── .gitignore ├── CODE_OF_CONDUCT.md ├── .idea ├── .idea.ReSharperPlugin.CyclomaticComplexity │ └── .idea │ │ └── runConfigurations │ │ ├── Rider__Frontend__Unix_.xml │ │ ├── rdgen__Unix_.xml │ │ ├── rdgen__Windows_.xml │ │ ├── Rider__Frontend__Windows_.xml │ │ ├── VisualStudio.xml │ │ ├── Rider__Unix_.xml │ │ └── Rider__Windows_.xml ├── .idea.CyclomaticComplexity │ └── .idea │ │ └── runConfigurations │ │ ├── Rider__Windows_.xml │ │ ├── rdgen__Windows_.xml │ │ ├── VisualStudio.xml │ │ ├── rdgen__Unix_.xml │ │ └── Rider__Unix_.xml └── runConfigurations │ ├── Rider.xml │ └── rdgen.xml ├── publishPlugin.ps1 ├── settings.gradle.kts ├── gradle.properties ├── protocol ├── src │ └── main │ │ └── kotlin │ │ └── model │ │ └── rider │ │ └── CyclomaticComplexityModel.kt └── build.gradle.kts ├── settings.ps1 ├── CHANGELOG.md ├── CyclomaticComplexity.sln ├── runVisualStudio.ps1 ├── README.md ├── gradlew.bat └── LICENSE /dependencies.json: -------------------------------------------------------------------------------- 1 | [ 2 | ] 3 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/resharper-cyclomatic-complexity/HEAD/logo.png -------------------------------------------------------------------------------- /docs/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/resharper-cyclomatic-complexity/HEAD/docs/info.png -------------------------------------------------------------------------------- /tools/7za.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/resharper-cyclomatic-complexity/HEAD/tools/7za.exe -------------------------------------------------------------------------------- /tools/nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/resharper-cyclomatic-complexity/HEAD/tools/nuget.exe -------------------------------------------------------------------------------- /docs/options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/resharper-cyclomatic-complexity/HEAD/docs/options.png -------------------------------------------------------------------------------- /docs/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/resharper-cyclomatic-complexity/HEAD/docs/warning.png -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.100", 4 | "rollForward": "latestMajor" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tools/vswhere.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/resharper-cyclomatic-complexity/HEAD/tools/vswhere.exe -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/resharper-cyclomatic-complexity/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/rider/main/resources/messages/CyclomaticComplexityBundle.properties: -------------------------------------------------------------------------------- 1 | configurable.name.cyclomaticcomplexity.options.title=Cyclomatic Complexity -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Preserve line endings in gradle scripts 5 | gradlew* -text diff 6 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/ComplexityError.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/resharper-cyclomatic-complexity/HEAD/src/dotnet/PowerToys.CyclomaticComplexity/ComplexityError.png -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/ComplexityWarning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JetBrains/resharper-cyclomatic-complexity/HEAD/src/dotnet/PowerToys.CyclomaticComplexity/ComplexityWarning.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build artifacts 2 | /.intellijPlatform/ 3 | [Bb]in/ 4 | [Oo]bj/ 5 | build 6 | output 7 | .gradle 8 | packages 9 | .tmp 10 | .nuke 11 | 12 | # User-specific files 13 | *.suo 14 | *.user 15 | *.sln.docstates 16 | *.cache 17 | 18 | # IDEs 19 | .idea 20 | .vs 21 | _ReSharper* 22 | _dotTrace* 23 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-8.13-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Code of Conduct 4 | 5 | This project and the corresponding community is governed by the [JetBrains Open Source and Community Code of Conduct](https://confluence.jetbrains.com/display/ALL/JetBrains+Open+Source+and+Community+Code+of+Conduct). Please make sure you read it. 6 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/ZoneMarker.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Application.BuildScript.Application.Zones; 2 | using JetBrains.ReSharper.Resources.Shell; 3 | #if RIDER 4 | using JetBrains.Rider.Backend.Env; 5 | #endif 6 | 7 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity 8 | { 9 | [ZoneMarker] 10 | public class ZoneMarker : 11 | #if RIDER 12 | IRequire, 13 | #endif 14 | IRequire 15 | { 16 | } 17 | } -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/CyclomaticComplexity.dotSettings: -------------------------------------------------------------------------------- 1 | 2 | WARNING -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | kotlin = "2.1.20" # https://plugins.jetbrains.com/docs/intellij/using-kotlin.html#kotlin-standard-library 3 | rdGen = "2025.3.1" # https://github.com/JetBrains/rd/releases 4 | 5 | [libraries] 6 | kotlinStdLib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } 7 | rdGen = { group = "com.jetbrains.rd", name = "rd-gen", version.ref = "rdGen" } 8 | 9 | [plugins] 10 | kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 11 | -------------------------------------------------------------------------------- /.idea/.idea.ReSharperPlugin.CyclomaticComplexity/.idea/runConfigurations/Rider__Frontend__Unix_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /.idea/.idea.CyclomaticComplexity/.idea/runConfigurations/Rider__Windows_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/.idea.ReSharperPlugin.CyclomaticComplexity/.idea/runConfigurations/rdgen__Unix_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/.idea.CyclomaticComplexity/.idea/runConfigurations/rdgen__Windows_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /.idea/.idea.ReSharperPlugin.CyclomaticComplexity/.idea/runConfigurations/rdgen__Windows_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/.idea.ReSharperPlugin.CyclomaticComplexity/.idea/runConfigurations/Rider__Frontend__Windows_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /src/rider/main/kotlin/com/jetbrains/rider/plugins/cyclomaticcomplexity/options/CyclomaticComplexityOptionsPage.kt: -------------------------------------------------------------------------------- 1 | package com.jetbrains.rider.plugins.cyclomaticcomplexity.options 2 | 3 | import com.jetbrains.rider.plugins.cyclomaticcomplexity.CyclomaticComplexityBundle 4 | import com.jetbrains.rider.settings.simple.SimpleOptionsPage 5 | 6 | class CyclomaticComplexityOptionsPage : SimpleOptionsPage( 7 | name = CyclomaticComplexityBundle.message("configurable.name.cyclomaticcomplexity.options.title"), 8 | pageId = "PowerToys.CyclomaticComplexity") 9 | { 10 | override fun getId(): String { 11 | return "PowerToys.CyclomaticComplexity" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.idea/.idea.CyclomaticComplexity/.idea/runConfigurations/VisualStudio.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/TestEnvironment.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using JetBrains.Application.BuildScript.Application.Zones; 3 | using JetBrains.ReSharper.Psi; 4 | using JetBrains.ReSharper.TestFramework; 5 | using JetBrains.TestFramework; 6 | using JetBrains.TestFramework.Application.Zones; 7 | using NUnit.Framework; 8 | 9 | [assembly: Apartment(ApartmentState.STA)] 10 | 11 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity.Tests 12 | { 13 | [ZoneDefinition] 14 | public interface ICyclomaticComplexityTestZone : ITestsEnvZone, IRequire 15 | { 16 | } 17 | 18 | [SetUpFixture] 19 | public class TestEnvironment : ExtensionTestEnvironmentAssembly 20 | { 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.idea/.idea.CyclomaticComplexity/.idea/runConfigurations/rdgen__Unix_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/CSharp/WorkaroundBoolAsConditional.cs: -------------------------------------------------------------------------------- 1 | public class Foo 2 | { 3 | public bool Blah { get; set; } 4 | 5 | // C# treats bool values and negation operators as conditionals, 6 | // bumping the CC incorrectly 7 | public void Thing(bool val) 8 | { 9 | Blah = val; 10 | Blah = val; 11 | Blah = val; 12 | Blah = val; 13 | Blah = val; 14 | Blah = val; 15 | Blah = val; 16 | Blah = !val; 17 | Blah = !val; 18 | Blah = !val; 19 | Blah = !val; 20 | Blah = !val; 21 | Blah = !val; 22 | Blah = !val; 23 | Blah = !val; 24 | Blah = !val; 25 | Blah = !val; 26 | Blah = !val; 27 | Blah = !val; 28 | Blah = !val; 29 | Blah = !val; 30 | Blah = !val; 31 | Blah = !val; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/.idea.CyclomaticComplexity/.idea/runConfigurations/Rider__Unix_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Rider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/rider/main/kotlin/com/jetbrains/rider/plugins/cyclomaticcomplexity/CyclomaticComplexityBundle.kt: -------------------------------------------------------------------------------- 1 | package com.jetbrains.rider.plugins.cyclomaticcomplexity 2 | 3 | import com.intellij.DynamicBundle 4 | import org.jetbrains.annotations.Nls 5 | import org.jetbrains.annotations.NonNls 6 | import org.jetbrains.annotations.PropertyKey 7 | 8 | class CyclomaticComplexityBundle : DynamicBundle(BUNDLE) { 9 | companion object { 10 | @NonNls 11 | private const val BUNDLE = "messages.CyclomaticComplexityBundle" 12 | private val INSTANCE: CyclomaticComplexityBundle = CyclomaticComplexityBundle() 13 | 14 | @Nls 15 | fun message( 16 | @PropertyKey(resourceBundle = BUNDLE) key: String, 17 | vararg params: Any 18 | ): String { 19 | return INSTANCE.getMessage(key, *params) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.idea/.idea.ReSharperPlugin.CyclomaticComplexity/.idea/runConfigurations/VisualStudio.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/IComplexityHighlighting.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using JetBrains.ReSharper.Feature.Services.Daemon; 18 | 19 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity 20 | { 21 | public interface IComplexityHighlighting : IHighlighting 22 | { 23 | } 24 | } -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/AnonymousMethodWithDefaultSettings.js: -------------------------------------------------------------------------------- 1 | (function(age, name, isAdmin) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | })(); 40 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/AnonymousMethodWithModifiedSettings.js: -------------------------------------------------------------------------------- 1 | (function(age, name, isAdmin) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | })(); 40 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/NamedMethodWithDefaultSettings.js: -------------------------------------------------------------------------------- 1 | function ComplexMethod(age, name, isAdmin) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | } 40 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/NamedMethodWithModifiedSettings.js: -------------------------------------------------------------------------------- 1 | function ComplexMethod(age, name, isAdmin) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | } 40 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/AssignedFunctionWithDefaultSettings.js: -------------------------------------------------------------------------------- 1 | var myFunction = function(age, name, isAdmin) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | } 40 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/AssignedFunctionWithModifiedSettings.js: -------------------------------------------------------------------------------- 1 | var myFunction = function(age, name, isAdmin) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | } 40 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/TypeScript/AnonymousMethodWithDefaultSettings.ts: -------------------------------------------------------------------------------- 1 | var foo = function(age, name, isAdmin) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | } 40 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/TypeScript/AnonymousMethodWithModifiedSettings.ts: -------------------------------------------------------------------------------- 1 | var foo = function(age, name, isAdmin) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | } 40 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/TypeScript/ModuleMethodWithDefaultSettings.ts: -------------------------------------------------------------------------------- 1 | function ComplexMethod(age, name, isAdmin) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | } 40 | -------------------------------------------------------------------------------- /src/dotnet/Plugin.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2025.3.0.1 4 | 5 | CyclomaticComplexity 6 | Calculates cyclomatic complexity for C#, VB, JavaScript, TypeScript and C++. Highlights any method that exceeds a configurable threshold 7 | Author 8 | Copyright 2006-$([System.DateTime]::Now.Year) JetBrains 9 | cyclomatic complexity analysis 10 | JetBrains, Matt Ellis 11 | https://github.com/JetBrains/resharper-cyclomatic-complexity 12 | https://github.com/JetBrains/resharper-cyclomatic-complexity/blob/master/LICENSE 13 | https://raw.githubusercontent.com/JetBrains/resharper-cyclomatic-complexity/master/logo.png 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/TypeScript/ModuleMethodWithModifiedSettings.ts: -------------------------------------------------------------------------------- 1 | function ComplexMethod(age, name, isAdmin) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | } 40 | -------------------------------------------------------------------------------- /publishPlugin.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | [string]$Configuration = "Release", 3 | [Parameter(Mandatory=$true)] 4 | [string]$Version, 5 | [Parameter(Mandatory=$true)] 6 | [string]$ApiKey 7 | ) 8 | 9 | Set-StrictMode -Version Latest 10 | $ErrorActionPreference = "Stop" 11 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 12 | Set-Location $PSScriptRoot 13 | 14 | . ".\settings.ps1" 15 | 16 | $ChangelogText = ([Regex]::Matches([System.IO.File]::ReadAllText("$PSScriptRoot\CHANGELOG.md"), '(?s)(##.+?.+?)(?=##|$)').Captures | Select -First 10) -Join '' 17 | 18 | Invoke-Exe $MSBuildPath "/t:Restore;Rebuild;Pack" "$SolutionPath" "/v:minimal" "/p:Configuration=$Configuration" "/p:PackageOutputPath=$OutputDirectory" "/p:PackageVersion=$Version" "/p:PackageReleaseNotes=`"$ChangelogText`"" 19 | $PackageFile = "$OutputDirectory\$PluginId.$Version*.nupkg" 20 | Invoke-Exe $NuGetPath push $PackageFile -Source "https://plugins.jetbrains.com/api/v2/package" -ApiKey $ApiKey 21 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | // Provide repositories to resolve plugins 3 | repositories { 4 | maven { setUrl("https://cache-redirector.jetbrains.com/plugins.gradle.org") } 5 | maven { setUrl("https://cache-redirector.jetbrains.com/maven-central") } 6 | maven { setUrl("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") } 7 | } 8 | resolutionStrategy { 9 | eachPlugin { 10 | // Gradle has to map a plugin dependency to Maven coordinates - '{groupId}:{artifactId}:{version}'. It tries 11 | // to do use '{plugin.id}:{plugin.id}.gradle.plugin:version'. 12 | // This doesn't work for rdgen, so we provide some help 13 | if (requested.id.id == "com.jetbrains.rdgen") { 14 | useModule("com.jetbrains.rd:rd-gen:${requested.version}") 15 | } 16 | } 17 | } 18 | } 19 | 20 | rootProject.name = "ReSharperPlugin.CyclomaticComplexity" 21 | 22 | include(":protocol") 23 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Any property can be overwritten from command-line via 2 | # -P= 3 | 4 | DotnetPluginId=PowerToys.CyclomaticComplexity 5 | DotnetSolution=CyclomaticComplexity.sln 6 | RiderPluginId=cyclomaticcomplexity 7 | PluginVersion=9999.0.0 8 | 9 | BuildConfiguration=Debug 10 | 11 | PublishToken="_PLACEHOLDER_" 12 | 13 | # See https://www.jetbrains.com/intellij-repository/snapshots 14 | # Keep in sync with SdkVersion in Plugin.props --> 15 | # 16 | # Examples: 17 | # Release: 2020.2 18 | # EAP: 2020.3-EAP2-SNAPSHOT 19 | # Nightly: 2020.3-SNAPSHOT 20 | ProductVersion=2025.3 21 | 22 | # Kotlin 1.4 will bundle the stdlib dependency by default, causing problems with the version bundled with the IDE 23 | # https://blog.jetbrains.com/kotlin/2020/07/kotlin-1-4-rc-released/#stdlib-default 24 | kotlin.stdlib.default.dependency=false 25 | 26 | # Required to download Rider artifacts from Maven (and not "binary" releases from CDN). 27 | org.jetbrains.intellij.platform.buildFeature.useBinaryReleases=false 28 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/TypeScript/ClassMethodWithDefaultSettings.ts: -------------------------------------------------------------------------------- 1 | class MyClass { 2 | ComplexMethod(age : int, name : string, isAdmin : bool) : bool { 3 | var result = false; 4 | var value = false; 5 | 6 | if (name === "Sarah") { 7 | if (age < 20 || age > 100) { 8 | if (isAdmin) { 9 | result = true; 10 | value = true; 11 | } 12 | else if (age == 42) { 13 | result = false; 14 | } 15 | else { 16 | result = true; 17 | } 18 | } 19 | } 20 | else if (name == "Gentry" && isAdmin) { 21 | result = false; 22 | } 23 | else { 24 | if (age == 50) { 25 | if (isAdmin) { 26 | if (name == "Amrit") { 27 | result = false; 28 | } 29 | else if (name == "Jane") { 30 | result = true; 31 | } 32 | else { 33 | result = false; 34 | } 35 | } 36 | } 37 | } 38 | 39 | return result; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/TypeScript/ClassMethodWithModifiedSettings.ts: -------------------------------------------------------------------------------- 1 | class MyClass { 2 | ComplexMethod(age : int, name : string, isAdmin : bool) : bool { 3 | var result = false; 4 | var value = false; 5 | 6 | if (name === "Sarah") { 7 | if (age < 20 || age > 100) { 8 | if (isAdmin) { 9 | result = true; 10 | value = true; 11 | } 12 | else if (age == 42) { 13 | result = false; 14 | } 15 | else { 16 | result = true; 17 | } 18 | } 19 | } 20 | else if (name == "Gentry" && isAdmin) { 21 | result = false; 22 | } 23 | else { 24 | if (age == 50) { 25 | if (isAdmin) { 26 | if (name == "Amrit") { 27 | result = false; 28 | } 29 | else if (name == "Jane") { 30 | result = true; 31 | } 32 | else { 33 | result = false; 34 | } 35 | } 36 | } 37 | } 38 | 39 | return result; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.idea/runConfigurations/rdgen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | false 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/NestedFunctionWithDefaultSettings.js: -------------------------------------------------------------------------------- 1 | function outerFunction() { 2 | var myFunction = function(age, name, isAdmin) { 3 | var result = false; 4 | var value = false; 5 | 6 | if (name === "Sarah") { 7 | if (age < 20 || age > 100) { 8 | if (isAdmin) { 9 | result = true; 10 | value = true; 11 | } 12 | else if (age == 42) { 13 | result = false; 14 | } 15 | else { 16 | result = true; 17 | } 18 | } 19 | } 20 | else if (name == "Gentry" && isAdmin) { 21 | result = false; 22 | } 23 | else { 24 | if (age == 50) { 25 | if (isAdmin) { 26 | if (name == "Amrit") { 27 | result = false; 28 | } 29 | else if (name == "Jane") { 30 | result = true; 31 | } 32 | else { 33 | result = false; 34 | } 35 | } 36 | } 37 | } 38 | 39 | return result; 40 | } 41 | myFunction(42, "Matt", true); 42 | } 43 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/NestedFunctionWithModifiedSettings.js: -------------------------------------------------------------------------------- 1 | function outerFunction() { 2 | var myFunction = function(age, name, isAdmin) { 3 | var result = false; 4 | var value = false; 5 | 6 | if (name === "Sarah") { 7 | if (age < 20 || age > 100) { 8 | if (isAdmin) { 9 | result = true; 10 | value = true; 11 | } 12 | else if (age == 42) { 13 | result = false; 14 | } 15 | else { 16 | result = true; 17 | } 18 | } 19 | } 20 | else if (name == "Gentry" && isAdmin) { 21 | result = false; 22 | } 23 | else { 24 | if (age == 50) { 25 | if (isAdmin) { 26 | if (name == "Amrit") { 27 | result = false; 28 | } 29 | else if (name == "Jane") { 30 | result = true; 31 | } 32 | else { 33 | result = false; 34 | } 35 | } 36 | } 37 | } 38 | 39 | return result; 40 | } 41 | myFunction(42, "Matt", true); 42 | } 43 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/CSharp/WorkaroundBoolAsConditional.cs.gold: -------------------------------------------------------------------------------- 1 | public class |Foo|(0) 2 | { 3 | public bool Blah { get; set; } 4 | 5 | // C# treats bool values and negation operators as conditionals, 6 | // bumping the CC incorrectly 7 | public void |Thing|(1)(bool val) 8 | { 9 | Blah = val; 10 | Blah = val; 11 | Blah = val; 12 | Blah = val; 13 | Blah = val; 14 | Blah = val; 15 | Blah = val; 16 | Blah = !val; 17 | Blah = !val; 18 | Blah = !val; 19 | Blah = !val; 20 | Blah = !val; 21 | Blah = !val; 22 | Blah = !val; 23 | Blah = !val; 24 | Blah = !val; 25 | Blah = !val; 26 | Blah = !val; 27 | Blah = !val; 28 | Blah = !val; 29 | Blah = !val; 30 | Blah = !val; 31 | Blah = !val; 32 | } 33 | } 34 | 35 | --------------------------------------------------------- 36 | (0): Cyclomatic Complexity Highlight: Class 'Foo' has cyclomatic complexity of 1 (5% of threshold) 37 | (1): Cyclomatic Complexity Highlight: Method 'Thing' has cyclomatic complexity of 1 (5% of threshold) 38 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/AnonymousMethodWithDefaultSettings.js.gold: -------------------------------------------------------------------------------- 1 | (|function(age, name, isAdmin)|(0) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | })(); 40 | 41 | --------------------------------------------------------- 42 | (0): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (60% of threshold) 43 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/AssignedFunctionWithDefaultSettings.js.gold: -------------------------------------------------------------------------------- 1 | var myFunction = |function(age, name, isAdmin)|(0) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | } 40 | 41 | --------------------------------------------------------- 42 | (0): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (60% of threshold) 43 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/TypeScript/AnonymousMethodWithDefaultSettings.ts.gold: -------------------------------------------------------------------------------- 1 | var foo = |function(age, name, isAdmin)|(0) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | } 40 | 41 | --------------------------------------------------------- 42 | (0): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (60% of threshold) 43 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/Resources/DefaultSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 20 7 | 20 8 | 20 9 | 20 10 | 20 11 | 12 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/NamedMethodWithDefaultSettings.js.gold: -------------------------------------------------------------------------------- 1 | function |ComplexMethod|(0)(age, name, isAdmin) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | } 40 | 41 | --------------------------------------------------------- 42 | (0): Cyclomatic Complexity Highlight: Property 'ComplexMethod' has cyclomatic complexity of 12 (60% of threshold) 43 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/TypeScript/ModuleMethodWithDefaultSettings.ts.gold: -------------------------------------------------------------------------------- 1 | function |ComplexMethod|(0)(age, name, isAdmin) { 2 | var result = false; 3 | var value = false; 4 | 5 | if (name === "Sarah") { 6 | if (age < 20 || age > 100) { 7 | if (isAdmin) { 8 | result = true; 9 | value = true; 10 | } 11 | else if (age == 42) { 12 | result = false; 13 | } 14 | else { 15 | result = true; 16 | } 17 | } 18 | } 19 | else if (name == "Gentry" && isAdmin) { 20 | result = false; 21 | } 22 | else { 23 | if (age == 50) { 24 | if (isAdmin) { 25 | if (name == "Amrit") { 26 | result = false; 27 | } 28 | else if (name == "Jane") { 29 | result = true; 30 | } 31 | else { 32 | result = false; 33 | } 34 | } 35 | } 36 | } 37 | 38 | return result; 39 | } 40 | 41 | --------------------------------------------------------- 42 | (0): Cyclomatic Complexity Highlight: Exported function 'ComplexMethod' has cyclomatic complexity of 12 (60% of threshold) 43 | -------------------------------------------------------------------------------- /src/rider/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | CyclomaticComplexity 3 | CyclomaticComplexity 4 | _PLACEHOLDER_ 5 | JetBrains 6 | 7 | com.intellij.modules.rider 8 | 9 | messages.CyclomaticComplexityBundle 10 | 11 | 12 | 18 | 19 | 20 | 21 | Calculates cyclomatic complexity for C#, VB, and C++. Highlights any method that exceeds a configurable threshold

23 | ]]> 24 |
25 |
26 | -------------------------------------------------------------------------------- /protocol/src/main/kotlin/model/rider/CyclomaticComplexityModel.kt: -------------------------------------------------------------------------------- 1 | package model.rider 2 | 3 | import com.jetbrains.rider.model.nova.ide.SolutionModel 4 | import com.jetbrains.rider.generator.nova.* 5 | import com.jetbrains.rider.generator.nova.PredefinedType.* 6 | import com.jetbrains.rider.generator.nova.csharp.CSharp50Generator 7 | import com.jetbrains.rider.generator.nova.kotlin.Kotlin11Generator 8 | 9 | @Suppress("unused") 10 | object CyclomaticComplexityModel : Ext(SolutionModel.Solution) { 11 | 12 | val MyEnum = enum { 13 | +"FirstValue" 14 | +"SecondValue" 15 | } 16 | 17 | val MyStructure = structdef { 18 | field("projectFile", string) 19 | field("target", string) 20 | } 21 | 22 | init { 23 | //setting(CSharp50Generator.Namespace, "ReSharperPlugin.CyclomaticComplexity.Rider.Model") 24 | //setting(Kotlin11Generator.Namespace, "com.jetbrains.rider.cyclomaticcomplexity.model") 25 | 26 | property("myString", string) 27 | property("myBool", bool) 28 | property("myEnum", MyEnum.nullable) 29 | 30 | map("data", string, string) 31 | 32 | signal("myStructure", MyStructure) 33 | } 34 | } -------------------------------------------------------------------------------- /.idea/.idea.ReSharperPlugin.CyclomaticComplexity/.idea/runConfigurations/Rider__Unix_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/.idea.ReSharperPlugin.CyclomaticComplexity/.idea/runConfigurations/Rider__Windows_.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | 18 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/CyclomaticComplexityAnalysisSettings.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using JetBrains.Application.Settings; 18 | using JetBrains.ReSharper.Resources.Settings; 19 | 20 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity 21 | { 22 | [SettingsKey(typeof (CodeInspectionSettings), "Cyclomatic complexity analysis")] 23 | public class CyclomaticComplexityAnalysisSettings 24 | { 25 | public const int DefaultThreshold = 20; 26 | 27 | [SettingsIndexedEntry("Threshold per language")] 28 | public IIndexedEntry Thresholds; 29 | } 30 | } -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/TypeScript/ClassMethodWithDefaultSettings.ts.gold: -------------------------------------------------------------------------------- 1 | class MyClass { 2 | |ComplexMethod|(0)(age : int, name : string, isAdmin : bool) : bool { 3 | var result = false; 4 | var value = false; 5 | 6 | if (name === "Sarah") { 7 | if (age < 20 || age > 100) { 8 | if (isAdmin) { 9 | result = true; 10 | value = true; 11 | } 12 | else if (age == 42) { 13 | result = false; 14 | } 15 | else { 16 | result = true; 17 | } 18 | } 19 | } 20 | else if (name == "Gentry" && isAdmin) { 21 | result = false; 22 | } 23 | else { 24 | if (age == 50) { 25 | if (isAdmin) { 26 | if (name == "Amrit") { 27 | result = false; 28 | } 29 | else if (name == "Jane") { 30 | result = true; 31 | } 32 | else { 33 | result = false; 34 | } 35 | } 36 | } 37 | } 38 | 39 | return result; 40 | } 41 | } 42 | 43 | --------------------------------------------------------- 44 | (0): Cyclomatic Complexity Highlight: Function member 'ComplexMethod' has cyclomatic complexity of 12 (60% of threshold) 45 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/Cpp/MethodWithDefaultSettings.cpp: -------------------------------------------------------------------------------- 1 | #define true 1 2 | #define false 0 3 | 4 | int ComplexMethod(int age, char *name, int isAdmin) 5 | { 6 |     int result = false; 7 | int value = false; 8 |   9 |     if (name == "Sarah") 10 |     { 11 |         if (age < 20 || age > 100) 12 |         { 13 |             if (isAdmin) 14 |             { 15 |                 result = true; 16 | value = true; 17 |             } 18 |             else if (age == 42) 19 |             { 20 |                 result = false; 21 |             } 22 |             else 23 |             { 24 |                 result = true; 25 |             } 26 |         } 27 |     } 28 |     else if (name == "Gentry" && isAdmin) 29 |     { 30 |         result = false; 31 |     } 32 |     else 33 |     { 34 |         if (age == 50) 35 |         { 36 |             if (isAdmin) 37 |             { 38 |                 if (name == "Amrit") 39 |                 { 40 |                     result = false; 41 |                 } 42 |                 else if (name == "Jane") 43 |                 { 44 |                     result = true; 45 |                 } 46 |                 else 47 |                 { 48 |                     result = false; 49 |                 } 50 |             } 51 |         } 52 |     } 53 |   54 |     return result; 55 | } 56 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/Cpp/MethodWithModifiedSettings.cpp: -------------------------------------------------------------------------------- 1 | #define true 1 2 | #define false 0 3 | 4 | int ComplexMethod(int age, char *name, int isAdmin) 5 | { 6 |     int result = false; 7 | int value = false; 8 |   9 |     if (name == "Sarah") 10 |     { 11 |         if (age < 20 || age > 100) 12 |         { 13 |             if (isAdmin) 14 |             { 15 |                 result = true; 16 | value = true; 17 |             } 18 |             else if (age == 42) 19 |             { 20 |                 result = false; 21 |             } 22 |             else 23 |             { 24 |                 result = true; 25 |             } 26 |         } 27 |     } 28 |     else if (name == "Gentry" && isAdmin) 29 |     { 30 |         result = false; 31 |     } 32 |     else 33 |     { 34 |         if (age == 50) 35 |         { 36 |             if (isAdmin) 37 |             { 38 |                 if (name == "Amrit") 39 |                 { 40 |                     result = false; 41 |                 } 42 |                 else if (name == "Jane") 43 |                 { 44 |                     result = true; 45 |                 } 46 |                 else 47 |                 { 48 |                     result = false; 49 |                 } 50 |             } 51 |         } 52 |     } 53 |   54 |     return result; 55 | } 56 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/Options/ComplexityAnalysisOptionsViewModel.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using System.Collections.Generic; 18 | using System.Collections.ObjectModel; 19 | using JetBrains.Application.UI.UIAutomation; 20 | 21 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity.Options 22 | { 23 | public class ComplexityAnalysisOptionsViewModel : AAutomation 24 | { 25 | public ComplexityAnalysisOptionsViewModel(List list) 26 | { 27 | PerLanguageProperties = new ObservableCollection(list); 28 | } 29 | 30 | public ObservableCollection PerLanguageProperties { get; private set; } 31 | } 32 | } -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/NestedFunctionWithDefaultSettings.js.gold: -------------------------------------------------------------------------------- 1 | function |outerFunction|(0)() { 2 | var myFunction = |function(age, name, isAdmin)|(1) { 3 | var result = false; 4 | var value = false; 5 | 6 | if (name === "Sarah") { 7 | if (age < 20 || age > 100) { 8 | if (isAdmin) { 9 | result = true; 10 | value = true; 11 | } 12 | else if (age == 42) { 13 | result = false; 14 | } 15 | else { 16 | result = true; 17 | } 18 | } 19 | } 20 | else if (name == "Gentry" && isAdmin) { 21 | result = false; 22 | } 23 | else { 24 | if (age == 50) { 25 | if (isAdmin) { 26 | if (name == "Amrit") { 27 | result = false; 28 | } 29 | else if (name == "Jane") { 30 | result = true; 31 | } 32 | else { 33 | result = false; 34 | } 35 | } 36 | } 37 | } 38 | 39 | return result; 40 | } 41 | myFunction(42, "Matt", true); 42 | } 43 | 44 | --------------------------------------------------------- 45 | (0): Cyclomatic Complexity Highlight: Property 'outerFunction' has cyclomatic complexity of 1 (5% of threshold) 46 | (1): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (60% of threshold) 47 | -------------------------------------------------------------------------------- /src/dotnet/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Latest 5 | MSB3277 6 | true 7 | false 8 | None 9 | 10 | obj\$(MSBuildProjectName)\ 11 | $(DefaultItemExcludes);obj\** 12 | bin\$(MSBuildProjectName)\$(Configuration)\ 13 | 14 | 15 | 16 | TRACE;DEBUG;JET_MODE_ASSERT 17 | 18 | 19 | 20 | 21 | 22 | $(SdkVersion.Substring(2,2))$(SdkVersion.Substring(5,1)).0.0 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | JetResourceGenerator 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/Cpp/MethodWithDefaultSettings.cpp.gold: -------------------------------------------------------------------------------- 1 | #define true 1 2 | #define false 0 3 | 4 | |int ComplexMethod(int age, char *name, int isAdmin)|(0) 5 | { 6 |     int result = false; 7 | int value = false; 8 |   9 |     if (name == "Sarah") 10 |     { 11 |         if (age < 20 || age > 100) 12 |         { 13 |             if (isAdmin) 14 |             { 15 |                 result = true; 16 | value = true; 17 |             } 18 |             else if (age == 42) 19 |             { 20 |                 result = false; 21 |             } 22 |             else 23 |             { 24 |                 result = true; 25 |             } 26 |         } 27 |     } 28 |     else if (name == "Gentry" && isAdmin) 29 |     { 30 |         result = false; 31 |     } 32 |     else 33 |     { 34 |         if (age == 50) 35 |         { 36 |             if (isAdmin) 37 |             { 38 |                 if (name == "Amrit") 39 |                 { 40 |                     result = false; 41 |                 } 42 |                 else if (name == "Jane") 43 |                 { 44 |                     result = true; 45 |                 } 46 |                 else 47 |                 { 48 |                     result = false; 49 |                 } 50 |             } 51 |         } 52 |     } 53 |   54 |     return result; 55 | } 56 | 57 | --------------------------------------------------------- 58 | (0): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (60% of threshold) 59 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/ComplexityAnalysisInvalidateOnThresholdChange.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using JetBrains.Application.Components; 18 | using JetBrains.Application.Parts; 19 | using JetBrains.Application.Settings; 20 | using JetBrains.Lifetimes; 21 | using JetBrains.ProjectModel; 22 | using JetBrains.ReSharper.Feature.Services.Daemon; 23 | 24 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity 25 | { 26 | [SolutionComponent(Instantiation.DemandAnyThreadSafe)] 27 | public class ComplexityAnalysisInvalidateOnThresholdChange : IStartupActivity 28 | { 29 | public ComplexityAnalysisInvalidateOnThresholdChange(Lifetime lifetime, IDaemon daemon, ISettingsStore settingsStore) 30 | { 31 | var settingsKey = settingsStore.Schema.GetKey(); 32 | settingsStore.AdviseChange(lifetime, settingsKey, daemon.Invalidate); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/PowerToys.CyclomaticComplexity.Rider.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildToolsPath)\Microsoft.CSharp.targets 5 | 6 | 7 | 8 | 9 | 10 | net472 11 | PowerToys.CyclomaticComplexity 12 | ReSharperPlugin.CyclomaticComplexity 13 | false 14 | $(DefineConstants);RIDER 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Gray;GrayDark;Color 28 | True 29 | Designer 30 | MSBuild:Compile 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /protocol/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.jetbrains.rd.generator.gradle.RdGenTask 2 | 3 | plugins { 4 | id("org.jetbrains.kotlin.jvm") 5 | id("com.jetbrains.rdgen") version libs.versions.rdGen 6 | } 7 | 8 | dependencies { 9 | implementation(libs.kotlinStdLib) 10 | implementation(libs.rdGen) 11 | implementation( 12 | project( 13 | mapOf( 14 | "path" to ":", 15 | "configuration" to "riderModel" 16 | ) 17 | ) 18 | ) 19 | } 20 | 21 | val DotnetPluginId: String by rootProject 22 | val RiderPluginId: String by rootProject 23 | 24 | rdgen { 25 | val csOutput = File(rootDir, "src/dotnet/${DotnetPluginId}") 26 | val ktOutput = File(rootDir, "src/rider/main/kotlin/com/jetbrains/rider/plugins/${RiderPluginId.replace('.','/').toLowerCase()}") 27 | 28 | verbose = true 29 | packages = "model.rider" 30 | 31 | generator { 32 | language = "kotlin" 33 | transform = "asis" 34 | root = "com.jetbrains.rider.model.nova.ide.IdeRoot" 35 | namespace = "com.jetbrains.rider.model" 36 | directory = "$ktOutput" 37 | } 38 | 39 | generator { 40 | language = "csharp" 41 | transform = "reversed" 42 | root = "com.jetbrains.rider.model.nova.ide.IdeRoot" 43 | namespace = "JetBrains.Rider.Model" 44 | directory = "$csOutput" 45 | } 46 | } 47 | 48 | tasks.withType { 49 | val classPath = sourceSets["main"].runtimeClasspath 50 | dependsOn(classPath) 51 | classpath(classPath) 52 | } 53 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/DefaultCyclomaticComplexitySettings.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using System.IO; 18 | using System.Reflection; 19 | using JetBrains.Application.Settings; 20 | using JetBrains.Diagnostics; 21 | using JetBrains.Lifetimes; 22 | using JetBrains.ReSharper.Resources.Settings; 23 | 24 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity 25 | { 26 | [DefaultSettings(typeof (CodeInspectionSettings))] 27 | public class DefaultCyclomaticComplexitySettings : IHaveDefaultSettingsStream 28 | { 29 | public string Name => "Default Cyclomatic Complexity Settings"; 30 | 31 | public Stream GetDefaultSettingsStream(Lifetime lifetime) 32 | { 33 | var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("PowerToys.CyclomaticComplexity.CyclomaticComplexity.dotSettings"); 34 | Assertion.Assert(stream != null, "stream != null"); 35 | lifetime.AddDispose(stream); 36 | return stream; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/PowerToys.CyclomaticComplexity.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net472 5 | false 6 | ReSharperPlugin.CyclomaticComplexity.Tests 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ..\..\..\..\..\Users\matthias.koch\.nuget\packages\jetbrains.psi.features.test.framework\191.0.20190429.185738\DotFiles\JetBrains.ReSharper.TestFramework.dll 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /settings.ps1: -------------------------------------------------------------------------------- 1 | $PluginId = "PowerToys.CyclomaticComplexity" 2 | $SolutionPath = "$PSScriptRoot\CyclomaticComplexity.sln" 3 | $SourceBasePath = "$PSScriptRoot\src\dotnet" 4 | 5 | $VsWhereOutput = [xml] (& "$PSScriptRoot\tools\vswhere.exe" -format xml -products *) 6 | $VisualStudio = $VsWhereOutput.instances.instance | 7 | Where-Object { $_.channelId -match "Release" } | 8 | Sort-Object -Property installationVersion | 9 | Select-Object -Last 1 10 | 11 | $VisualStudioBaseDirectory = $VisualStudio.installationPath 12 | $VisualStudioMajorVersion = ($VisualStudio.installationVersion -split '\.')[0] 13 | $VisualStudioInstanceId = $VisualStudio.instanceId 14 | $DevEnvPath = Get-ChildItem "$VisualStudioBaseDirectory\*\IDE\devenv.exe" 15 | $MSBuildPath = Get-ChildItem "$VisualStudioBaseDirectory\MSBuild\*\Bin\MSBuild.exe" 16 | 17 | $OutputDirectory = "$PSScriptRoot\output" 18 | $NuGetPath = "$PSScriptRoot\tools\nuget.exe" 19 | 20 | Function Invoke-Exe { 21 | param( 22 | [parameter(mandatory=$true,position=0)] [ValidateNotNullOrEmpty()] [string] $Executable, 23 | [Parameter(ValueFromRemainingArguments=$true)][String[]] $Arguments, 24 | [parameter(mandatory=$false)] [array] $ValidExitCodes = @(0) 25 | ) 26 | 27 | Write-Host "> $Executable $Arguments" 28 | $rc = Start-Process -FilePath $Executable -ArgumentList $Arguments -NoNewWindow -Passthru 29 | $rc.Handle # to initialize handle according to https://stackoverflow.com/a/23797762/2684760 30 | $rc.WaitForExit() 31 | if (-Not $ValidExitCodes.Contains($rc.ExitCode)) { 32 | throw "'$Executable $Arguments' failed with exit code $($rc.ExitCode), valid exit codes: $ValidExitCodes" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/Options/LanguageSpecificComplexityProperty.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using JetBrains.Application.Settings; 18 | using JetBrains.Application.UI.Controls.TreeListView; 19 | using JetBrains.Application.UI.Options; 20 | using JetBrains.DataFlow; 21 | using JetBrains.Lifetimes; 22 | 23 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity.Options 24 | { 25 | public class LanguageSpecificComplexityProperty : ObservableObject 26 | { 27 | public LanguageSpecificComplexityProperty(Lifetime lifetime, OptionsSettingsSmartContext settings, SettingsIndexedEntry settingsIndexedEntry, string index, string languagePresentableName, int defaultValue) 28 | { 29 | Name = languagePresentableName; 30 | 31 | Threshold = new Property(lifetime, index); 32 | Threshold.Change.Advise(lifetime, () => OnPropertyChanged(nameof(Threshold))); 33 | settings.SetBinding(lifetime, settingsIndexedEntry, index, Threshold, defaultValue); 34 | } 35 | 36 | public string Name { get; private set; } 37 | public IProperty Threshold { get; set; } 38 | } 39 | } -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/CSharp/ComplexMethodWithDefaultSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | class ComplexityExample 4 | { 5 | // From http://dontcodetired.com/blog/post/Using-Cyclomatic-Complexity-as-an-Indicator-of-Clean-Code.aspx 6 | // Used by permission :) https://twitter.com/robertsjason/status/618041977997627392 7 | public bool SomeComplexMethod(int age, string name, bool isAdmin) 8 | { 9 |     bool result = false; 10 | bool value = false; 11 |   12 |     if (name == "Sarah") 13 |     { 14 |         if (age < 20 || age > 100) 15 |         { 16 |             if (isAdmin) 17 |             { 18 |                 result = true; 19 | value = true; 20 |             } 21 |             else if (age == 42) 22 |             { 23 |                 result = false; 24 |             } 25 |             else 26 |             { 27 |                 result = true; 28 |             } 29 |         } 30 |     } 31 |     else if (name == "Gentry" && isAdmin) 32 |     { 33 |         result = false; 34 |     } 35 |     else 36 |     { 37 |         if (age == 50) 38 |         { 39 |             if (isAdmin) 40 |             { 41 |                 if (name == "Amrit") 42 |                 { 43 |                     result = false; 44 |                 } 45 |                 else if (name == "Jane") 46 |                 { 47 |                     result = true; 48 |                 } 49 |                 else 50 |                 { 51 |                     result = false; 52 |                 } 53 |             } 54 |         } 55 |     } 56 |   57 |     return result; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/CSharp/ComplexMethodWithModifiedSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | class ComplexityExample 4 | { 5 | // From http://dontcodetired.com/blog/post/Using-Cyclomatic-Complexity-as-an-Indicator-of-Clean-Code.aspx 6 | // Used by permission :) https://twitter.com/robertsjason/status/618041977997627392 7 | public bool SomeComplexMethod(int age, string name, bool isAdmin) 8 | { 9 |     bool result = false; 10 | bool value = false; 11 |   12 |     if (name == "Sarah") 13 |     { 14 |         if (age < 20 || age > 100) 15 |         { 16 |             if (isAdmin) 17 |             { 18 |                 result = true; 19 | value = true; 20 |             } 21 |             else if (age == 42) 22 |             { 23 |                 result = false; 24 |             } 25 |             else 26 |             { 27 |                 result = true; 28 |             } 29 |         } 30 |     } 31 |     else if (name == "Gentry" && isAdmin) 32 |     { 33 |         result = false; 34 |     } 35 |     else 36 |     { 37 |         if (age == 50) 38 |         { 39 |             if (isAdmin) 40 |             { 41 |                 if (name == "Amrit") 42 |                 { 43 |                     result = false; 44 |                 } 45 |                 else if (name == "Jane") 46 |                 { 47 |                     result = true; 48 |                 } 49 |                 else 50 |                 { 51 |                     result = false; 52 |                 } 53 |             } 54 |         } 55 |     } 56 |   57 |     return result; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/Options/HyperlinkOptionViewModel.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using System.Collections.Generic; 18 | using System.Windows.Input; 19 | using JetBrains.Application.UI.Options; 20 | using JetBrains.Application.UI.Options.OptionsDialog.SimpleOptions; 21 | using JetBrains.DataFlow; 22 | using JetBrains.Lifetimes; 23 | 24 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity.Options 25 | { 26 | public class HyperlinkOptionViewModel : OptionEntityPrimitive, IOptionCanBeEnabled, IOptionCanBeVisible 27 | { 28 | public HyperlinkOptionViewModel(Lifetime lifetime, string text, ICommand command) 29 | : base(lifetime) 30 | { 31 | Text = text; 32 | Command = command; 33 | 34 | IsEnabledProperty = new Property(lifetime, "IsEnabledProperty") { Value = true }; 35 | IsVisibleProperty = new Property(lifetime, "IsVisibleProperty") { Value = true }; 36 | } 37 | 38 | public string Text { get; } 39 | public ICommand Command { get; set; } 40 | 41 | public new IProperty IsEnabledProperty { get; } 42 | public new IProperty IsVisibleProperty { get; } 43 | 44 | public override IEnumerable GetKeywords() 45 | { 46 | yield return new OptionsPageKeyword(Text); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/PowerToys.CyclomaticComplexity.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildToolsPath)\Microsoft.CSharp.targets 5 | 6 | 7 | 8 | 9 | 10 | net472 11 | True 12 | ReSharperPlugin.CyclomaticComplexity 13 | $(DefineConstants);RESHARPER 14 | false 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | Gray;GrayDark;Color 40 | True 41 | Designer 42 | MSBuild:Compile 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/ComplexityWarningHighlight.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using JetBrains.DocumentModel; 18 | using JetBrains.ReSharper.Feature.Services.Daemon; 19 | using JetBrains.ReSharper.Plugins.CyclomaticComplexity; 20 | using JetBrains.ReSharper.Psi; 21 | 22 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity 23 | { 24 | 25 | [RegisterConfigurableSeverity(SeverityId, 26 | null, HighlightingGroupIds.CodeSmell, 27 | "Element exceeds cyclomatic complexity", 28 | @"The cyclomatic complexity of the code element exceeds the configured threshold. 29 | You can configure the thresholds in the Cyclomatic Complexity options page.", 30 | Severity.WARNING)] 31 | [ConfigurableSeverityHighlighting(SeverityId, KnownLanguage.ANY_LANGUAGEID, OverlapResolve = OverlapResolveKind.WARNING)] 32 | public class ComplexityWarningHighlight : IComplexityHighlighting 33 | { 34 | public const string SeverityId = "CyclomaticComplexity"; 35 | 36 | private readonly DocumentRange range; 37 | 38 | public ComplexityWarningHighlight(string tooltip, DocumentRange range) 39 | { 40 | ToolTip = tooltip; 41 | this.range = range; 42 | } 43 | 44 | public bool IsValid() => true; 45 | public DocumentRange CalculateRange() => range; 46 | public string ToolTip { get; } 47 | public string ErrorStripeToolTip => ToolTip; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/CSharp/ComplexMethodWithDefaultSettings.cs.gold: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | class |ComplexityExample|(0) 4 | { 5 | // From http://dontcodetired.com/blog/post/Using-Cyclomatic-Complexity-as-an-Indicator-of-Clean-Code.aspx 6 | // Used by permission :) https://twitter.com/robertsjason/status/618041977997627392 7 | public bool |SomeComplexMethod|(1)(int age, string name, bool isAdmin) 8 | { 9 |     bool result = false; 10 | bool value = false; 11 |   12 |     if (name == "Sarah") 13 |     { 14 |         if (age < 20 || age > 100) 15 |         { 16 |             if (isAdmin) 17 |             { 18 |                 result = true; 19 | value = true; 20 |             } 21 |             else if (age == 42) 22 |             { 23 |                 result = false; 24 |             } 25 |             else 26 |             { 27 |                 result = true; 28 |             } 29 |         } 30 |     } 31 |     else if (name == "Gentry" && isAdmin) 32 |     { 33 |         result = false; 34 |     } 35 |     else 36 |     { 37 |         if (age == 50) 38 |         { 39 |             if (isAdmin) 40 |             { 41 |                 if (name == "Amrit") 42 |                 { 43 |                     result = false; 44 |                 } 45 |                 else if (name == "Jane") 46 |                 { 47 |                     result = true; 48 |                 } 49 |                 else 50 |                 { 51 |                     result = false; 52 |                 } 53 |             } 54 |         } 55 |     } 56 |   57 |     return result; 58 | } 59 | } 60 | 61 | --------------------------------------------------------- 62 | (0): Cyclomatic Complexity Highlight: Class 'ComplexityExample' has cyclomatic complexity of 1 (5% of threshold) 63 | (1): Cyclomatic Complexity Highlight: Method 'SomeComplexMethod' has cyclomatic complexity of 12 (60% of threshold) 64 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/TypeScriptComplexityHighlightingTest.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Application.Settings; 2 | using JetBrains.ReSharper.Feature.Services.Daemon; 3 | using JetBrains.ReSharper.FeaturesTestFramework.Daemon; 4 | using JetBrains.ReSharper.Psi; 5 | using JetBrains.ReSharper.TestFramework; 6 | using NUnit.Framework; 7 | 8 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity.Tests 9 | { 10 | [TestSettingsKey(typeof(CyclomaticComplexityAnalysisSettings))] 11 | public class TypeScriptComplexityHighlightingTest : TypeScriptHighlightingTestBase 12 | { 13 | protected override string RelativeTestDataPath => "TypeScript"; 14 | 15 | protected override bool HighlightingPredicate(IHighlighting highlighting, IPsiSourceFile sourceFile, 16 | IContextBoundSettingsStore settingsStore) 17 | { 18 | return highlighting is IComplexityHighlighting; 19 | } 20 | 21 | [Test] 22 | public void TestClassMethodWithDefaultSettings() 23 | { 24 | DoOneTest("ClassMethodWithDefaultSettings"); 25 | } 26 | 27 | [Test] 28 | [TestSettings("{ Thresholds: [ { 'TYPE_SCRIPT': 10 }, { 'TYPE_SCRIPT': 21 }, { 'TYPE_SCRIPT': 30 } ] }")] 29 | public void TestClassMethodWithNonDefaultSettings() 30 | { 31 | DoOneTest("ClassMethodWithModifiedSettings"); 32 | } 33 | 34 | [Test] 35 | public void TestModuleMethodWithDefaultSettings() 36 | { 37 | DoOneTest("ModuleMethodWithDefaultSettings"); 38 | } 39 | 40 | [Test] 41 | [TestSettings("{ Thresholds: [ { 'TYPE_SCRIPT': 10 }, { 'TYPE_SCRIPT': 21 }, { 'TYPE_SCRIPT': 30 } ] }")] 42 | public void TestModuleMethodWithNonDefaultSettings() 43 | { 44 | DoOneTest("ModuleMethodWithModifiedSettings"); 45 | } 46 | 47 | [Test] 48 | public void TestAnonynousMethodWithDefaultSettings() 49 | { 50 | DoOneTest("AnonymousMethodWithDefaultSettings"); 51 | } 52 | 53 | [Test] 54 | [TestSettings("{ Thresholds: [ { 'TYPE_SCRIPT': 10 }, { 'TYPE_SCRIPT': 21 }, { 'TYPE_SCRIPT': 30 } ] }")] 55 | public void TestAnonymousMethodWithNonDefaultSettings() 56 | { 57 | DoOneTest("AnonymousMethodWithModifiedSettings"); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## 2025.3.0 8 | - Added support for ReSharper and Rider 2025.3 9 | 10 | ## 2025.2.0 11 | - Added support for ReSharper and Rider 2025.2 12 | 13 | ## 2025.1.0 14 | - Added support for ReSharper and Rider 2025.1 15 | 16 | ## 2024.3.0 17 | - Added support for ReSharper and Rider 2024.3 18 | - Fixed default settings marker 19 | 20 | ## 2024.2.0 21 | - Added support for ReSharper and Rider 2024.2 22 | 23 | ## 2024.1.0 24 | - Added support for ReSharper and Rider 2024.1 25 | 26 | ## 2023.3.0 27 | - Added support for ReSharper and Rider 2023.3 28 | 29 | ## 2023.2.0 30 | - Added support for ReSharper and Rider 2023.2 31 | 32 | ## 2023.1.0 33 | - Added support for ReSharper and Rider 2023.1 34 | 35 | ## 2022.3.0 36 | - Added support for ReSharper and Rider 2022.3 37 | 38 | ## 2022.2.0 39 | - Added support for ReSharper and Rider 2022.2 40 | 41 | ## 2022.1.0 42 | - Added support for ReSharper and Rider 2022.1 43 | 44 | ## 2021.3.0 45 | - Added support for ReSharper and Rider 2021.3 46 | 47 | ## 2021.2.0 48 | - Added support for ReSharper and Rider 2021.2 49 | 50 | ## 2021.1.0 51 | - Added support for ReSharper and Rider 2021.1 52 | 53 | ## 2020.3.0 54 | - Added support for ReSharper and Rider 2020.3 55 | 56 | ## 2020.2.1 57 | - Fixed plugin to signal that IDE restart is required 58 | 59 | ## 2020.2.0 60 | - Added support for ReSharper and Rider 2020.2 61 | 62 | ## 2020.1.0 63 | - Added support for ReSharper and Rider 2020.1 64 | 65 | ## 2019.3.0 66 | - Added support for ReSharper and Rider 2019.3 67 | 68 | ## 2019.2.0 69 | - Added support for ReSharper and Rider 2019.2 70 | 71 | ## 2019.1.0 72 | - Added support for ReSharper and Rider 2019.1 73 | 74 | ## 2018.3.4 75 | - Fixed NRE 76 | - Fixed DocumentRange to be declaration name 77 | 78 | ## 2018.3.3 79 | - Added CodeVision support 80 | 81 | ## 2018.3.2 82 | - Fixed icons for ReSharper 83 | 84 | ## 2018.3.1 85 | - Common UI for settings 86 | 87 | ## 2018.3.0 88 | - Support for ReSharper 2018.3 89 | 90 | ## 2018.1.0 91 | - Support for ReSharper 2018.1 92 | 93 | ## 2017.3.0 94 | - Support for ReSharper 2017.3 95 | 96 | ## 2017.2.0 97 | - Support for ReSharper 2017.2 98 | 99 | ## 2017.1.0 100 | - Support for ReSharper 2017.1 101 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/CppComplexityHighlightingTest.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using System; 18 | using System.Collections.Generic; 19 | using JetBrains.Metadata.Reader.API; 20 | using JetBrains.ProjectModel; 21 | using JetBrains.ProjectModel.Properties; 22 | using JetBrains.ProjectModel.Properties.VCXProj; 23 | using JetBrains.ReSharper.Feature.Services.Daemon; 24 | using JetBrains.ReSharper.FeaturesTestFramework.Daemon; 25 | using JetBrains.ReSharper.Psi; 26 | using JetBrains.ReSharper.Psi.Cpp.Language; 27 | using JetBrains.ReSharper.TestFramework; 28 | using NUnit.Framework; 29 | using PlatformID = JetBrains.Application.platforms.PlatformID; 30 | 31 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity.Tests 32 | { 33 | [TestSettingsKey(typeof(CyclomaticComplexityAnalysisSettings))] 34 | [TestFileExtension(CppProjectFileType.CPP_EXTENSION)] 35 | public class CppComplexityHighlightingTest : HighlightingTestBase 36 | { 37 | protected override string RelativeTestDataPath => "Cpp"; 38 | 39 | protected override bool HighlightingPredicate(IHighlighting highlighting, IPsiSourceFile sourceFile) 40 | { 41 | return highlighting is IComplexityHighlighting; 42 | } 43 | 44 | public override IProjectProperties GetProjectProperties (PlatformID platformId, IReadOnlyCollection targetFrameworkIds, ICollection flavours) 45 | { 46 | return VCXProjectPropertiesFactory.CreateVCXProjectProperties(platformId, flavours, targetFrameworkIds); 47 | } 48 | 49 | protected override PsiLanguageType CompilerIdsLanguage => CppLanguage.Instance; 50 | 51 | [Test] 52 | public void TestMethodWithDefaultSettings() 53 | { 54 | DoOneTest("MethodWithDefaultSettings"); 55 | } 56 | 57 | [Test] 58 | [TestSettings("{ Thresholds: [ { 'CPP': 10 }, { 'CPP': 21 }, { 'CPP': 30 } ] }")] 59 | public void TestMethodWithNonDefaultSettings() 60 | { 61 | DoOneTest("MethodWithModifiedSettings"); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /CyclomaticComplexity.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerToys.CyclomaticComplexity", "src\dotnet\PowerToys.CyclomaticComplexity\PowerToys.CyclomaticComplexity.csproj", "{05608FE6-4FD1-4E9D-9BE2-B13A0E370BA2}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerToys.CyclomaticComplexity.Rider", "src\dotnet\PowerToys.CyclomaticComplexity\PowerToys.CyclomaticComplexity.Rider.csproj", "{084172D1-A9C6-46D0-96AD-05C5B09A5E5D}" 6 | EndProject 7 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{4A9ABB95-3762-448B-B5BF-099E46DB22DE}" 8 | ProjectSection(SolutionItems) = preProject 9 | src\dotnet\Plugin.props = src\dotnet\Plugin.props 10 | README.md = README.md 11 | CHANGELOG.md = CHANGELOG.md 12 | src\rider\main\resources\META-INF\plugin.xml = src\rider\main\resources\META-INF\plugin.xml 13 | gradle.properties = gradle.properties 14 | src\dotnet\Directory.Build.props = src\dotnet\Directory.Build.props 15 | build.gradle.kts = build.gradle.kts 16 | EndProjectSection 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerToys.CyclomaticComplexity.Tests", "src\dotnet\PowerToys.CyclomaticComplexity.Tests\PowerToys.CyclomaticComplexity.Tests.csproj", "{6B2CDBAE-6AF4-4893-8DA9-0A1B2FDAC94A}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {05608FE6-4FD1-4E9D-9BE2-B13A0E370BA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {05608FE6-4FD1-4E9D-9BE2-B13A0E370BA2}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {05608FE6-4FD1-4E9D-9BE2-B13A0E370BA2}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {05608FE6-4FD1-4E9D-9BE2-B13A0E370BA2}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {084172D1-A9C6-46D0-96AD-05C5B09A5E5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {084172D1-A9C6-46D0-96AD-05C5B09A5E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {084172D1-A9C6-46D0-96AD-05C5B09A5E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {084172D1-A9C6-46D0-96AD-05C5B09A5E5D}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {6B2CDBAE-6AF4-4893-8DA9-0A1B2FDAC94A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {6B2CDBAE-6AF4-4893-8DA9-0A1B2FDAC94A}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {6B2CDBAE-6AF4-4893-8DA9-0A1B2FDAC94A}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {6B2CDBAE-6AF4-4893-8DA9-0A1B2FDAC94A}.Release|Any CPU.Build.0 = Release|Any CPU 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/CSharpComplexityHighlightingTest.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using JetBrains.Application.Settings; 18 | using JetBrains.ReSharper.Feature.Services.Daemon; 19 | using JetBrains.ReSharper.FeaturesTestFramework.Daemon; 20 | using JetBrains.ReSharper.Psi; 21 | using JetBrains.ReSharper.TestFramework; 22 | using NUnit.Framework; 23 | 24 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity.Tests 25 | { 26 | [TestSettingsKey(typeof(CyclomaticComplexityAnalysisSettings))] 27 | public class CSharpComplexityHighlightingTest : CSharpHighlightingTestBase 28 | { 29 | protected override string RelativeTestDataPath => "CSharp"; 30 | 31 | protected override bool HighlightingPredicate(IHighlighting highlighting, IPsiSourceFile sourceFile, 32 | IContextBoundSettingsStore settingsStore) 33 | { 34 | return highlighting is IComplexityHighlighting; 35 | } 36 | 37 | protected override void DoNamedTest2(params string[] auxFiles) 38 | { 39 | ExecuteWithinSettingsTransaction(store => 40 | { 41 | // This will be done in the plugin by adding a .dotSettings file 42 | RunGuarded(() => store.SetIndexedValue(HighlightingSettingsAccessor.InspectionSeverities, "FunctionComplexityOverflow", Severity.WARNING)); 43 | base.DoNamedTest2(auxFiles); 44 | }); 45 | } 46 | 47 | [Test] 48 | public void TestComplexMethodWithDefaultSettings() 49 | { 50 | DoOneTest("ComplexMethodWithDefaultSettings"); 51 | } 52 | 53 | [Test] 54 | [TestSettings("{ Thresholds: [ { 'CSHARP': 10 }, { 'CSHARP': 21 }, { 'CSHARP': 30 } ] }")] 55 | public void TestComplexMethodWithNonDefaultSettings() 56 | { 57 | DoOneTest("ComplexMethodWithModifiedSettings"); 58 | } 59 | 60 | [Test] 61 | public void TestMethodBodyTooComplexForValueAnalysisAndHighCyclomaticComplexity() 62 | { 63 | DoNamedTest2(); 64 | } 65 | 66 | [Test] 67 | public void TestMethodBodyTooComplexForValueAnalysisButLowCyclomaticComplexity() 68 | { 69 | DoNamedTest2(); 70 | } 71 | 72 | [Test] 73 | public void TestWorkaroundBoolAsConditional() 74 | { 75 | DoNamedTest2(); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/JsComplexityHighlightingTest.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using JetBrains.Application.Settings; 18 | using JetBrains.ReSharper.Feature.Services.Daemon; 19 | using JetBrains.ReSharper.FeaturesTestFramework.Daemon; 20 | using JetBrains.ReSharper.Psi; 21 | using JetBrains.ReSharper.TestFramework; 22 | using NUnit.Framework; 23 | 24 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity.Tests 25 | { 26 | [TestSettingsKey(typeof(CyclomaticComplexityAnalysisSettings))] 27 | public class JsComplexityHighlightingTest : JavaScriptHighlightingTestBase 28 | { 29 | protected override string RelativeTestDataPath => "JS"; 30 | 31 | protected override bool HighlightingPredicate(IHighlighting highlighting, IPsiSourceFile sourceFile, 32 | IContextBoundSettingsStore settingsStore) 33 | { 34 | return highlighting is IComplexityHighlighting; 35 | } 36 | 37 | [Test] 38 | public void TestNamedMethodWithDefaultSettings() 39 | { 40 | DoOneTest("NamedMethodWithDefaultSettings"); 41 | } 42 | 43 | [Test] 44 | [TestSettings("{ Thresholds: [ { 'JAVA_SCRIPT': 10 }, { 'JAVA_SCRIPT': 21 }, { 'JAVA_SCRIPT': 30 } ] }")] 45 | public void TestNamedMethodWithNonDefaultSettings() 46 | { 47 | DoOneTest("NamedMethodWithModifiedSettings"); 48 | } 49 | 50 | [Test] 51 | public void TestAnonynousMethodWithDefaultSettings() 52 | { 53 | DoOneTest("AnonymousMethodWithDefaultSettings"); 54 | } 55 | 56 | [Test] 57 | [TestSettings("{ Thresholds: [ { 'JAVA_SCRIPT': 10 }, { 'JAVA_SCRIPT': 21 }, { 'JAVA_SCRIPT': 30 } ] }")] 58 | public void TestAnonymousMethodWithNonDefaultSettings() 59 | { 60 | DoOneTest("AnonymousMethodWithModifiedSettings"); 61 | } 62 | 63 | [Test] 64 | public void TestAssignedFunctionWithDefaultSettings() 65 | { 66 | DoOneTest("AssignedFunctionWithDefaultSettings"); 67 | } 68 | 69 | [Test] 70 | [TestSettings("{ Thresholds: [ { 'JAVA_SCRIPT': 10 }, { 'JAVA_SCRIPT': 21 }, { 'JAVA_SCRIPT': 30 } ] }")] 71 | public void TestAssignedFunctionWithNonDefaultSettings() 72 | { 73 | DoOneTest("AssignedFunctionWithModifiedSettings"); 74 | } 75 | 76 | [Test] 77 | public void TestNestedFunctionWithDefaultSettings() 78 | { 79 | DoOneTest("NestedFunctionWithDefaultSettings"); 80 | } 81 | 82 | [Test] 83 | [TestSettings("{ Thresholds: [ { 'JAVA_SCRIPT': 10 }, { 'JAVA_SCRIPT': 21 }, { 'JAVA_SCRIPT': 30 } ] }")] 84 | public void TestNestedFunctionWithNonDefaultSettings() 85 | { 86 | DoOneTest("NestedFunctionWithModifiedSettings"); 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/AnonymousMethodWithModifiedSettings.js.gold: -------------------------------------------------------------------------------- 1 | Thresholds = JAVA_SCRIPT:10, 2 | (|function(age, name, isAdmin)|(0) { 3 | var result = false; 4 | var value = false; 5 | 6 | if (name === "Sarah") { 7 | if (age < 20 || age > 100) { 8 | if (isAdmin) { 9 | result = true; 10 | value = true; 11 | } 12 | else if (age == 42) { 13 | result = false; 14 | } 15 | else { 16 | result = true; 17 | } 18 | } 19 | } 20 | else if (name == "Gentry" && isAdmin) { 21 | result = false; 22 | } 23 | else { 24 | if (age == 50) { 25 | if (isAdmin) { 26 | if (name == "Amrit") { 27 | result = false; 28 | } 29 | else if (name == "Jane") { 30 | result = true; 31 | } 32 | else { 33 | result = false; 34 | } 35 | } 36 | } 37 | } 38 | 39 | return result; 40 | })(); 41 | 42 | --------------------------------------------------------- 43 | (0): ReSharper Warning: Element has cyclomatic complexity of 12 (120% of threshold) 44 | 45 | ================ 46 | Thresholds = JAVA_SCRIPT:21, 47 | (|function(age, name, isAdmin)|(0) { 48 | var result = false; 49 | var value = false; 50 | 51 | if (name === "Sarah") { 52 | if (age < 20 || age > 100) { 53 | if (isAdmin) { 54 | result = true; 55 | value = true; 56 | } 57 | else if (age == 42) { 58 | result = false; 59 | } 60 | else { 61 | result = true; 62 | } 63 | } 64 | } 65 | else if (name == "Gentry" && isAdmin) { 66 | result = false; 67 | } 68 | else { 69 | if (age == 50) { 70 | if (isAdmin) { 71 | if (name == "Amrit") { 72 | result = false; 73 | } 74 | else if (name == "Jane") { 75 | result = true; 76 | } 77 | else { 78 | result = false; 79 | } 80 | } 81 | } 82 | } 83 | 84 | return result; 85 | })(); 86 | 87 | --------------------------------------------------------- 88 | (0): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (57% of threshold) 89 | 90 | ================ 91 | Thresholds = JAVA_SCRIPT:30, 92 | (|function(age, name, isAdmin)|(0) { 93 | var result = false; 94 | var value = false; 95 | 96 | if (name === "Sarah") { 97 | if (age < 20 || age > 100) { 98 | if (isAdmin) { 99 | result = true; 100 | value = true; 101 | } 102 | else if (age == 42) { 103 | result = false; 104 | } 105 | else { 106 | result = true; 107 | } 108 | } 109 | } 110 | else if (name == "Gentry" && isAdmin) { 111 | result = false; 112 | } 113 | else { 114 | if (age == 50) { 115 | if (isAdmin) { 116 | if (name == "Amrit") { 117 | result = false; 118 | } 119 | else if (name == "Jane") { 120 | result = true; 121 | } 122 | else { 123 | result = false; 124 | } 125 | } 126 | } 127 | } 128 | 129 | return result; 130 | })(); 131 | 132 | --------------------------------------------------------- 133 | (0): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (40% of threshold) 134 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/CSharp/MethodBodyTooComplexForValueAnalysisButLowCyclomaticComplexity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class MethodBodyTooComplex 4 | { 5 | // This relies on an implementation detail of ReSharper's value analysis 6 | // It only tracks 84 variables in a method (any more starts to incur too 7 | // much cost). A warning is added that is normally set to "Do not show", 8 | // but since we're concerned with complexity, I think it's worth enabling 9 | public static void MethodBodyTooComplexForValueAnalysisButLowCyclomaticComplexity(object obj) 10 | { 11 | var t1 = obj as string; 12 | var t2 = obj as string; 13 | var t3 = obj as string; 14 | var t4 = obj as string; 15 | var t5 = obj as string; 16 | var t6 = obj as string; 17 | var t7 = obj as string; 18 | var t8 = obj as string; 19 | var t9 = obj as string; 20 | var t10 = obj as string; 21 | var t11 = obj as string; 22 | var t12 = obj as string; 23 | var t13 = obj as string; 24 | var t14 = obj as string; 25 | var t15 = obj as string; 26 | var t16 = obj as string; 27 | var t17 = obj as string; 28 | var t18 = obj as string; 29 | var t19 = obj as string; 30 | var t20 = obj as string; 31 | var t21 = obj as string; 32 | var t22 = obj as string; 33 | var t23 = obj as string; 34 | var t24 = obj as string; 35 | var t25 = obj as string; 36 | var t26 = obj as string; 37 | var t27 = obj as string; 38 | var t28 = obj as string; 39 | var t29 = obj as string; 40 | var t30 = obj as string; 41 | var t31 = obj as string; 42 | var t32 = obj as string; 43 | var t33 = obj as string; 44 | var t34 = obj as string; 45 | var t35 = obj as string; 46 | var t36 = obj as string; 47 | var t37 = obj as string; 48 | var t38 = obj as string; 49 | var t39 = obj as string; 50 | var t40 = obj as string; 51 | var t41 = obj as string; 52 | var t42 = obj as string; 53 | var t43 = obj as string; 54 | var t44 = obj as string; 55 | var t45 = obj as string; 56 | var t46 = obj as string; 57 | var t47 = obj as string; 58 | var t48 = obj as string; 59 | var t49 = obj as string; 60 | var t50 = obj as string; 61 | var t51 = obj as string; 62 | var t52 = obj as string; 63 | var t53 = obj as string; 64 | var t54 = obj as string; 65 | var t55 = obj as string; 66 | var t56 = obj as string; 67 | var t57 = obj as string; 68 | var t58 = obj as string; 69 | var t59 = obj as string; 70 | var t60 = obj as string; 71 | var t61 = obj as string; 72 | var t62 = obj as string; 73 | var t63 = obj as string; 74 | var t64 = obj as string; 75 | var t65 = obj as string; 76 | var t66 = obj as string; 77 | var t67 = obj as string; 78 | var t68 = obj as string; 79 | var t69 = obj as string; 80 | var t70 = obj as string; 81 | var t71 = obj as string; 82 | var t72 = obj as string; 83 | var t73 = obj as string; 84 | var t74 = obj as string; 85 | var t75 = obj as string; 86 | var t76 = obj as string; 87 | var t77 = obj as string; 88 | var t78 = obj as string; 89 | var t79 = obj as string; 90 | var t80 = obj as string; 91 | var t81 = obj as string; 92 | var t82 = obj as string; 93 | var t83 = obj as string; 94 | var t84 = obj as string; 95 | var t85 = obj as string; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/TypeScript/AnonymousMethodWithModifiedSettings.ts.gold: -------------------------------------------------------------------------------- 1 | Thresholds = TYPE_SCRIPT:10, 2 | var foo = |function(age, name, isAdmin)|(0) { 3 | var result = false; 4 | var value = false; 5 | 6 | if (name === "Sarah") { 7 | if (age < 20 || age > 100) { 8 | if (isAdmin) { 9 | result = true; 10 | value = true; 11 | } 12 | else if (age == 42) { 13 | result = false; 14 | } 15 | else { 16 | result = true; 17 | } 18 | } 19 | } 20 | else if (name == "Gentry" && isAdmin) { 21 | result = false; 22 | } 23 | else { 24 | if (age == 50) { 25 | if (isAdmin) { 26 | if (name == "Amrit") { 27 | result = false; 28 | } 29 | else if (name == "Jane") { 30 | result = true; 31 | } 32 | else { 33 | result = false; 34 | } 35 | } 36 | } 37 | } 38 | 39 | return result; 40 | } 41 | 42 | --------------------------------------------------------- 43 | (0): ReSharper Warning: Element has cyclomatic complexity of 12 (120% of threshold) 44 | 45 | ================ 46 | Thresholds = TYPE_SCRIPT:21, 47 | var foo = |function(age, name, isAdmin)|(0) { 48 | var result = false; 49 | var value = false; 50 | 51 | if (name === "Sarah") { 52 | if (age < 20 || age > 100) { 53 | if (isAdmin) { 54 | result = true; 55 | value = true; 56 | } 57 | else if (age == 42) { 58 | result = false; 59 | } 60 | else { 61 | result = true; 62 | } 63 | } 64 | } 65 | else if (name == "Gentry" && isAdmin) { 66 | result = false; 67 | } 68 | else { 69 | if (age == 50) { 70 | if (isAdmin) { 71 | if (name == "Amrit") { 72 | result = false; 73 | } 74 | else if (name == "Jane") { 75 | result = true; 76 | } 77 | else { 78 | result = false; 79 | } 80 | } 81 | } 82 | } 83 | 84 | return result; 85 | } 86 | 87 | --------------------------------------------------------- 88 | (0): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (57% of threshold) 89 | 90 | ================ 91 | Thresholds = TYPE_SCRIPT:30, 92 | var foo = |function(age, name, isAdmin)|(0) { 93 | var result = false; 94 | var value = false; 95 | 96 | if (name === "Sarah") { 97 | if (age < 20 || age > 100) { 98 | if (isAdmin) { 99 | result = true; 100 | value = true; 101 | } 102 | else if (age == 42) { 103 | result = false; 104 | } 105 | else { 106 | result = true; 107 | } 108 | } 109 | } 110 | else if (name == "Gentry" && isAdmin) { 111 | result = false; 112 | } 113 | else { 114 | if (age == 50) { 115 | if (isAdmin) { 116 | if (name == "Amrit") { 117 | result = false; 118 | } 119 | else if (name == "Jane") { 120 | result = true; 121 | } 122 | else { 123 | result = false; 124 | } 125 | } 126 | } 127 | } 128 | 129 | return result; 130 | } 131 | 132 | --------------------------------------------------------- 133 | (0): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (40% of threshold) 134 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/AssignedFunctionWithModifiedSettings.js.gold: -------------------------------------------------------------------------------- 1 | Thresholds = JAVA_SCRIPT:10, 2 | var myFunction = |function(age, name, isAdmin)|(0) { 3 | var result = false; 4 | var value = false; 5 | 6 | if (name === "Sarah") { 7 | if (age < 20 || age > 100) { 8 | if (isAdmin) { 9 | result = true; 10 | value = true; 11 | } 12 | else if (age == 42) { 13 | result = false; 14 | } 15 | else { 16 | result = true; 17 | } 18 | } 19 | } 20 | else if (name == "Gentry" && isAdmin) { 21 | result = false; 22 | } 23 | else { 24 | if (age == 50) { 25 | if (isAdmin) { 26 | if (name == "Amrit") { 27 | result = false; 28 | } 29 | else if (name == "Jane") { 30 | result = true; 31 | } 32 | else { 33 | result = false; 34 | } 35 | } 36 | } 37 | } 38 | 39 | return result; 40 | } 41 | 42 | --------------------------------------------------------- 43 | (0): ReSharper Warning: Element has cyclomatic complexity of 12 (120% of threshold) 44 | 45 | ================ 46 | Thresholds = JAVA_SCRIPT:21, 47 | var myFunction = |function(age, name, isAdmin)|(0) { 48 | var result = false; 49 | var value = false; 50 | 51 | if (name === "Sarah") { 52 | if (age < 20 || age > 100) { 53 | if (isAdmin) { 54 | result = true; 55 | value = true; 56 | } 57 | else if (age == 42) { 58 | result = false; 59 | } 60 | else { 61 | result = true; 62 | } 63 | } 64 | } 65 | else if (name == "Gentry" && isAdmin) { 66 | result = false; 67 | } 68 | else { 69 | if (age == 50) { 70 | if (isAdmin) { 71 | if (name == "Amrit") { 72 | result = false; 73 | } 74 | else if (name == "Jane") { 75 | result = true; 76 | } 77 | else { 78 | result = false; 79 | } 80 | } 81 | } 82 | } 83 | 84 | return result; 85 | } 86 | 87 | --------------------------------------------------------- 88 | (0): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (57% of threshold) 89 | 90 | ================ 91 | Thresholds = JAVA_SCRIPT:30, 92 | var myFunction = |function(age, name, isAdmin)|(0) { 93 | var result = false; 94 | var value = false; 95 | 96 | if (name === "Sarah") { 97 | if (age < 20 || age > 100) { 98 | if (isAdmin) { 99 | result = true; 100 | value = true; 101 | } 102 | else if (age == 42) { 103 | result = false; 104 | } 105 | else { 106 | result = true; 107 | } 108 | } 109 | } 110 | else if (name == "Gentry" && isAdmin) { 111 | result = false; 112 | } 113 | else { 114 | if (age == 50) { 115 | if (isAdmin) { 116 | if (name == "Amrit") { 117 | result = false; 118 | } 119 | else if (name == "Jane") { 120 | result = true; 121 | } 122 | else { 123 | result = false; 124 | } 125 | } 126 | } 127 | } 128 | 129 | return result; 130 | } 131 | 132 | --------------------------------------------------------- 133 | (0): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (40% of threshold) 134 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/NamedMethodWithModifiedSettings.js.gold: -------------------------------------------------------------------------------- 1 | Thresholds = JAVA_SCRIPT:10, 2 | function |ComplexMethod|(0)(age, name, isAdmin) { 3 | var result = false; 4 | var value = false; 5 | 6 | if (name === "Sarah") { 7 | if (age < 20 || age > 100) { 8 | if (isAdmin) { 9 | result = true; 10 | value = true; 11 | } 12 | else if (age == 42) { 13 | result = false; 14 | } 15 | else { 16 | result = true; 17 | } 18 | } 19 | } 20 | else if (name == "Gentry" && isAdmin) { 21 | result = false; 22 | } 23 | else { 24 | if (age == 50) { 25 | if (isAdmin) { 26 | if (name == "Amrit") { 27 | result = false; 28 | } 29 | else if (name == "Jane") { 30 | result = true; 31 | } 32 | else { 33 | result = false; 34 | } 35 | } 36 | } 37 | } 38 | 39 | return result; 40 | } 41 | 42 | --------------------------------------------------------- 43 | (0): ReSharper Warning: Property 'ComplexMethod' has cyclomatic complexity of 12 (120% of threshold) 44 | 45 | ================ 46 | Thresholds = JAVA_SCRIPT:21, 47 | function |ComplexMethod|(0)(age, name, isAdmin) { 48 | var result = false; 49 | var value = false; 50 | 51 | if (name === "Sarah") { 52 | if (age < 20 || age > 100) { 53 | if (isAdmin) { 54 | result = true; 55 | value = true; 56 | } 57 | else if (age == 42) { 58 | result = false; 59 | } 60 | else { 61 | result = true; 62 | } 63 | } 64 | } 65 | else if (name == "Gentry" && isAdmin) { 66 | result = false; 67 | } 68 | else { 69 | if (age == 50) { 70 | if (isAdmin) { 71 | if (name == "Amrit") { 72 | result = false; 73 | } 74 | else if (name == "Jane") { 75 | result = true; 76 | } 77 | else { 78 | result = false; 79 | } 80 | } 81 | } 82 | } 83 | 84 | return result; 85 | } 86 | 87 | --------------------------------------------------------- 88 | (0): Cyclomatic Complexity Highlight: Property 'ComplexMethod' has cyclomatic complexity of 12 (57% of threshold) 89 | 90 | ================ 91 | Thresholds = JAVA_SCRIPT:30, 92 | function |ComplexMethod|(0)(age, name, isAdmin) { 93 | var result = false; 94 | var value = false; 95 | 96 | if (name === "Sarah") { 97 | if (age < 20 || age > 100) { 98 | if (isAdmin) { 99 | result = true; 100 | value = true; 101 | } 102 | else if (age == 42) { 103 | result = false; 104 | } 105 | else { 106 | result = true; 107 | } 108 | } 109 | } 110 | else if (name == "Gentry" && isAdmin) { 111 | result = false; 112 | } 113 | else { 114 | if (age == 50) { 115 | if (isAdmin) { 116 | if (name == "Amrit") { 117 | result = false; 118 | } 119 | else if (name == "Jane") { 120 | result = true; 121 | } 122 | else { 123 | result = false; 124 | } 125 | } 126 | } 127 | } 128 | 129 | return result; 130 | } 131 | 132 | --------------------------------------------------------- 133 | (0): Cyclomatic Complexity Highlight: Property 'ComplexMethod' has cyclomatic complexity of 12 (40% of threshold) 134 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/TypeScript/ModuleMethodWithModifiedSettings.ts.gold: -------------------------------------------------------------------------------- 1 | Thresholds = TYPE_SCRIPT:10, 2 | function |ComplexMethod|(0)(age, name, isAdmin) { 3 | var result = false; 4 | var value = false; 5 | 6 | if (name === "Sarah") { 7 | if (age < 20 || age > 100) { 8 | if (isAdmin) { 9 | result = true; 10 | value = true; 11 | } 12 | else if (age == 42) { 13 | result = false; 14 | } 15 | else { 16 | result = true; 17 | } 18 | } 19 | } 20 | else if (name == "Gentry" && isAdmin) { 21 | result = false; 22 | } 23 | else { 24 | if (age == 50) { 25 | if (isAdmin) { 26 | if (name == "Amrit") { 27 | result = false; 28 | } 29 | else if (name == "Jane") { 30 | result = true; 31 | } 32 | else { 33 | result = false; 34 | } 35 | } 36 | } 37 | } 38 | 39 | return result; 40 | } 41 | 42 | --------------------------------------------------------- 43 | (0): ReSharper Warning: Exported function 'ComplexMethod' has cyclomatic complexity of 12 (120% of threshold) 44 | 45 | ================ 46 | Thresholds = TYPE_SCRIPT:21, 47 | function |ComplexMethod|(0)(age, name, isAdmin) { 48 | var result = false; 49 | var value = false; 50 | 51 | if (name === "Sarah") { 52 | if (age < 20 || age > 100) { 53 | if (isAdmin) { 54 | result = true; 55 | value = true; 56 | } 57 | else if (age == 42) { 58 | result = false; 59 | } 60 | else { 61 | result = true; 62 | } 63 | } 64 | } 65 | else if (name == "Gentry" && isAdmin) { 66 | result = false; 67 | } 68 | else { 69 | if (age == 50) { 70 | if (isAdmin) { 71 | if (name == "Amrit") { 72 | result = false; 73 | } 74 | else if (name == "Jane") { 75 | result = true; 76 | } 77 | else { 78 | result = false; 79 | } 80 | } 81 | } 82 | } 83 | 84 | return result; 85 | } 86 | 87 | --------------------------------------------------------- 88 | (0): Cyclomatic Complexity Highlight: Exported function 'ComplexMethod' has cyclomatic complexity of 12 (57% of threshold) 89 | 90 | ================ 91 | Thresholds = TYPE_SCRIPT:30, 92 | function |ComplexMethod|(0)(age, name, isAdmin) { 93 | var result = false; 94 | var value = false; 95 | 96 | if (name === "Sarah") { 97 | if (age < 20 || age > 100) { 98 | if (isAdmin) { 99 | result = true; 100 | value = true; 101 | } 102 | else if (age == 42) { 103 | result = false; 104 | } 105 | else { 106 | result = true; 107 | } 108 | } 109 | } 110 | else if (name == "Gentry" && isAdmin) { 111 | result = false; 112 | } 113 | else { 114 | if (age == 50) { 115 | if (isAdmin) { 116 | if (name == "Amrit") { 117 | result = false; 118 | } 119 | else if (name == "Jane") { 120 | result = true; 121 | } 122 | else { 123 | result = false; 124 | } 125 | } 126 | } 127 | } 128 | 129 | return result; 130 | } 131 | 132 | --------------------------------------------------------- 133 | (0): Cyclomatic Complexity Highlight: Exported function 'ComplexMethod' has cyclomatic complexity of 12 (40% of threshold) 134 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/Options/ComplexityAnalysisOptionPage.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using System.Collections.Generic; 18 | using System.Diagnostics; 19 | using System.Linq; 20 | using JetBrains.Application.UI.Commands; 21 | using JetBrains.Application.UI.Options; 22 | using JetBrains.Application.UI.Options.OptionsDialog; 23 | using JetBrains.DataFlow; 24 | using JetBrains.IDE.UI.Extensions; 25 | using JetBrains.IDE.UI.Extensions.Properties; 26 | using JetBrains.IDE.UI.Options; 27 | using JetBrains.Lifetimes; 28 | using JetBrains.ReSharper.Feature.Services.Daemon.OptionPages; 29 | using JetBrains.ReSharper.Psi; 30 | using JetBrains.ReSharper.Psi.ControlFlow; 31 | using JetBrains.ReSharper.Psi.JavaScript.WinRT.LanguageImpl; 32 | using JetBrains.Rider.Model.UIAutomation; 33 | using ReSharperPlugin.CyclomaticComplexity; 34 | 35 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity.Options 36 | { 37 | [OptionsPage(PageId, "Cyclomatic Complexity", typeof(CyclomaticComplexityThemedIcons.CyclomaticComplexity), ParentId = CodeInspectionPage.PID)] 38 | public class ComplexityAnalysisOptionPage : BeSimpleOptionsPage 39 | { 40 | private const string PageId = "PowerToys.CyclomaticComplexity"; 41 | 42 | public ComplexityAnalysisOptionPage( 43 | Lifetime lifetime, 44 | OptionsPageContext optionsPageContext, 45 | OptionsSettingsSmartContext optionsSettingsSmartContext, 46 | ILanguages languages, 47 | ILanguageManager languageManager) 48 | : base(lifetime, optionsPageContext, optionsSettingsSmartContext) 49 | { 50 | AddText("Specify cyclomatic complexity thresholds:"); 51 | 52 | var thresholds = OptionsSettingsSmartContext.Schema.GetIndexedEntry((CyclomaticComplexityAnalysisSettings s) => s.Thresholds); 53 | 54 | var list = new List(); 55 | foreach (var languageType in languages.All.Where(languageManager.HasService).OrderBy(GetPresentableName)) 56 | { 57 | var presentableName = GetPresentableName(languageType); 58 | var thing = new LanguageSpecificComplexityProperty(lifetime, optionsSettingsSmartContext, thresholds, languageType.Name, presentableName, CyclomaticComplexityAnalysisSettings.DefaultThreshold); 59 | list.Add(thing); 60 | } 61 | 62 | var treeGrid = list.GetBeList(lifetime, 63 | (l, e, p) => new List 64 | { 65 | e.Name.GetBeLabel(), 66 | e.Threshold.GetBeSpinner(lifetime, min: 1) 67 | }, 68 | new TreeConfiguration(new []{"Language,*", "Threshold,auto"})); 69 | 70 | AddControl(treeGrid, isStar: true); 71 | } 72 | 73 | private static string GetPresentableName(PsiLanguageType psiLanguageType) 74 | { 75 | // Bah, WinRT JS is a different language, that supports control flow, 76 | // but has the same presentable name as normal JS. I don't like 77 | // adding language specific fixes... 78 | if (psiLanguageType is JavaScriptWinRTLanguage) 79 | return "JavaScript (WinRT)"; 80 | return psiLanguageType.PresentableName; 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/TypeScript/ClassMethodWithModifiedSettings.ts.gold: -------------------------------------------------------------------------------- 1 | Thresholds = TYPE_SCRIPT:10, 2 | class MyClass { 3 | |ComplexMethod|(0)(age : int, name : string, isAdmin : bool) : bool { 4 | var result = false; 5 | var value = false; 6 | 7 | if (name === "Sarah") { 8 | if (age < 20 || age > 100) { 9 | if (isAdmin) { 10 | result = true; 11 | value = true; 12 | } 13 | else if (age == 42) { 14 | result = false; 15 | } 16 | else { 17 | result = true; 18 | } 19 | } 20 | } 21 | else if (name == "Gentry" && isAdmin) { 22 | result = false; 23 | } 24 | else { 25 | if (age == 50) { 26 | if (isAdmin) { 27 | if (name == "Amrit") { 28 | result = false; 29 | } 30 | else if (name == "Jane") { 31 | result = true; 32 | } 33 | else { 34 | result = false; 35 | } 36 | } 37 | } 38 | } 39 | 40 | return result; 41 | } 42 | } 43 | 44 | --------------------------------------------------------- 45 | (0): ReSharper Warning: Function member 'ComplexMethod' has cyclomatic complexity of 12 (120% of threshold) 46 | 47 | ================ 48 | Thresholds = TYPE_SCRIPT:21, 49 | class MyClass { 50 | |ComplexMethod|(0)(age : int, name : string, isAdmin : bool) : bool { 51 | var result = false; 52 | var value = false; 53 | 54 | if (name === "Sarah") { 55 | if (age < 20 || age > 100) { 56 | if (isAdmin) { 57 | result = true; 58 | value = true; 59 | } 60 | else if (age == 42) { 61 | result = false; 62 | } 63 | else { 64 | result = true; 65 | } 66 | } 67 | } 68 | else if (name == "Gentry" && isAdmin) { 69 | result = false; 70 | } 71 | else { 72 | if (age == 50) { 73 | if (isAdmin) { 74 | if (name == "Amrit") { 75 | result = false; 76 | } 77 | else if (name == "Jane") { 78 | result = true; 79 | } 80 | else { 81 | result = false; 82 | } 83 | } 84 | } 85 | } 86 | 87 | return result; 88 | } 89 | } 90 | 91 | --------------------------------------------------------- 92 | (0): Cyclomatic Complexity Highlight: Function member 'ComplexMethod' has cyclomatic complexity of 12 (57% of threshold) 93 | 94 | ================ 95 | Thresholds = TYPE_SCRIPT:30, 96 | class MyClass { 97 | |ComplexMethod|(0)(age : int, name : string, isAdmin : bool) : bool { 98 | var result = false; 99 | var value = false; 100 | 101 | if (name === "Sarah") { 102 | if (age < 20 || age > 100) { 103 | if (isAdmin) { 104 | result = true; 105 | value = true; 106 | } 107 | else if (age == 42) { 108 | result = false; 109 | } 110 | else { 111 | result = true; 112 | } 113 | } 114 | } 115 | else if (name == "Gentry" && isAdmin) { 116 | result = false; 117 | } 118 | else { 119 | if (age == 50) { 120 | if (isAdmin) { 121 | if (name == "Amrit") { 122 | result = false; 123 | } 124 | else if (name == "Jane") { 125 | result = true; 126 | } 127 | else { 128 | result = false; 129 | } 130 | } 131 | } 132 | } 133 | 134 | return result; 135 | } 136 | } 137 | 138 | --------------------------------------------------------- 139 | (0): Cyclomatic Complexity Highlight: Function member 'ComplexMethod' has cyclomatic complexity of 12 (40% of threshold) 140 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/CSharp/MethodBodyTooComplexForValueAnalysisButLowCyclomaticComplexity.cs.gold: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class |MethodBodyTooComplex|(0) 4 | { 5 | // This relies on an implementation detail of ReSharper's value analysis 6 | // It only tracks 84 variables in a method (any more starts to incur too 7 | // much cost). A warning is added that is normally set to "Do not show", 8 | // but since we're concerned with complexity, I think it's worth enabling 9 | public static void |MethodBodyTooComplexForValueAnalysisButLowCyclomaticComplexity|(1)(object obj) 10 | { 11 | var t1 = obj as string; 12 | var t2 = obj as string; 13 | var t3 = obj as string; 14 | var t4 = obj as string; 15 | var t5 = obj as string; 16 | var t6 = obj as string; 17 | var t7 = obj as string; 18 | var t8 = obj as string; 19 | var t9 = obj as string; 20 | var t10 = obj as string; 21 | var t11 = obj as string; 22 | var t12 = obj as string; 23 | var t13 = obj as string; 24 | var t14 = obj as string; 25 | var t15 = obj as string; 26 | var t16 = obj as string; 27 | var t17 = obj as string; 28 | var t18 = obj as string; 29 | var t19 = obj as string; 30 | var t20 = obj as string; 31 | var t21 = obj as string; 32 | var t22 = obj as string; 33 | var t23 = obj as string; 34 | var t24 = obj as string; 35 | var t25 = obj as string; 36 | var t26 = obj as string; 37 | var t27 = obj as string; 38 | var t28 = obj as string; 39 | var t29 = obj as string; 40 | var t30 = obj as string; 41 | var t31 = obj as string; 42 | var t32 = obj as string; 43 | var t33 = obj as string; 44 | var t34 = obj as string; 45 | var t35 = obj as string; 46 | var t36 = obj as string; 47 | var t37 = obj as string; 48 | var t38 = obj as string; 49 | var t39 = obj as string; 50 | var t40 = obj as string; 51 | var t41 = obj as string; 52 | var t42 = obj as string; 53 | var t43 = obj as string; 54 | var t44 = obj as string; 55 | var t45 = obj as string; 56 | var t46 = obj as string; 57 | var t47 = obj as string; 58 | var t48 = obj as string; 59 | var t49 = obj as string; 60 | var t50 = obj as string; 61 | var t51 = obj as string; 62 | var t52 = obj as string; 63 | var t53 = obj as string; 64 | var t54 = obj as string; 65 | var t55 = obj as string; 66 | var t56 = obj as string; 67 | var t57 = obj as string; 68 | var t58 = obj as string; 69 | var t59 = obj as string; 70 | var t60 = obj as string; 71 | var t61 = obj as string; 72 | var t62 = obj as string; 73 | var t63 = obj as string; 74 | var t64 = obj as string; 75 | var t65 = obj as string; 76 | var t66 = obj as string; 77 | var t67 = obj as string; 78 | var t68 = obj as string; 79 | var t69 = obj as string; 80 | var t70 = obj as string; 81 | var t71 = obj as string; 82 | var t72 = obj as string; 83 | var t73 = obj as string; 84 | var t74 = obj as string; 85 | var t75 = obj as string; 86 | var t76 = obj as string; 87 | var t77 = obj as string; 88 | var t78 = obj as string; 89 | var t79 = obj as string; 90 | var t80 = obj as string; 91 | var t81 = obj as string; 92 | var t82 = obj as string; 93 | var t83 = obj as string; 94 | var t84 = obj as string; 95 | var t85 = obj as string; 96 | } 97 | } 98 | 99 | --------------------------------------------------------- 100 | (0): Cyclomatic Complexity Highlight: Class 'MethodBodyTooComplex' has cyclomatic complexity of 1 (5% of threshold) 101 | (1): Cyclomatic Complexity Highlight: Method 'MethodBodyTooComplexForValueAnalysisButLowCyclomaticComplexity' has cyclomatic complexity of 1 (5% of threshold) 102 | -------------------------------------------------------------------------------- /docs/ThresholdGuidance.md: -------------------------------------------------------------------------------- 1 | # What threshold value should I choose? 2 | 3 | There is little consensus for a specific cyclomatic complexity value that indicates that a method has become too complex. As ever, it depends. Metrics are subjective, and should always be used as guidance, to inform a decision, rather than be blindly followed. Any threshold also needs to take into account the experience of the team that maintains the code. If they are an experienced team, you might be happier to allow somewhat more complex methods than if the team were less experienced. 4 | 5 | This thread on programmers.stackexchange.com goes into [more details about why this is a subjective metric](http://programmers.stackexchange.com/questions/194061/cyclomatic-complexity-ranges). 6 | 7 | This plugin uses a default value of 20, for all languages. Any method or function, etc. that has a complexity greater than 20 is flagged as too complex. 8 | 9 | To get some idea of ranges, here are some examples: 10 | 11 | 1. **NIST 500-235** - Structured Testing: A Testing Methodology Using the Cyclomatic Complexity Metric - September 1996 12 | http://www.mccabe.com/pdf/mccabe-nist235r.pdf 13 | Co-written by Thomas McCabe, who first introduced the cyclomatic complexity metric, this NIST paper suggests a limit of 10 (or maybe 15). See page 15: 14 | 15 | > The original limit of 10 as proposed by McCabe has significant supporting evidence, but limits as high as 15 have been used successfully as well. Limits over 10 should be reserved for projects that have several operational advantages over typical projects, for example experienced staff, formal design, a modern programming language, structured programming, code walkthroughs, and a comprehensive test plan. In other words, an organization can pick a complexity limit greater than 10, but only if it is sure it knows what it is doing and is willing to devote the additional testing effort required by more complex modules. 16 | 17 | 2. **C4 Software Technology Reference Guide** - 1997 18 | https://resources.sei.cmu.edu/asset_files/Handbook/1997_002_001_16523.pdf 19 | This Carnegie Mellon study offers a table, with a range of values. Anything over 20 is considered "complex, high risk", and is the basis for the default used in this plugin. Interestingly, this guide talks in terms of "risk", rather than "complexity". See page 145: 20 | 21 | > | **Cyclomatic Complexity** | **Risk Evaluation** | 22 | > |---------------------------|---------------------| 23 | > | 1 - 10 | a simple program, without much risk | 24 | > | 11 - 20 | more complex, moderate risk | 25 | > | 21 - 50 | complex, high risk | 26 | > | greater than 50 | untestable program (very high risk) | 27 | 28 | 3. **Code Complete** - Steve McConnell 29 | Steve McConnell also suggests a limit of 10. A value between 6 and 10 should be treated as a warning, and anything over 10 should be refactored. 30 | 31 | 4. **NDepend** - static analysis tool 32 | http://www.ndepend.com/docs/code-metrics#CC 33 | NDepend offers the following recommendation: 34 | 35 | > Methods where CC is higher than 15 are hard to understand and maintain. Methods where CC is higher than 30 are extremely complex and should be split into smaller methods (except if they are automatically generated by a tool). 36 | 37 | 5. **Microsoft Code Analysis** - static analysis tool 38 | https://msdn.microsoft.com/en-us/library/ms182212.aspx 39 | Microsoft's code analysis tool reports a cyclomatic complexity violation when the metric is greater than 25 40 | 41 | 6. **Avoid High Cyclomatic Complexity** - Phil Koopman blog post 42 | http://betterembsw.blogspot.co.uk/2014/06/avoid-high-cyclomatic-complexity.html 43 | A very informative blog post discussing cyclomatic complexity, including what it is, how to avoid it, and a round up of suggested values. 44 | 45 | This plugin uses a default value of 20. This is higher than McCabe's suggested 10 (or 15), but in line with Carnegie Mellon's suggested ranges. The value of 20 has been chosen to provide a useful initial value - not too low that a previously un-measured codebase has too many violations, and not too high that a program is too complex. 46 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/JS/NestedFunctionWithModifiedSettings.js.gold: -------------------------------------------------------------------------------- 1 | Thresholds = JAVA_SCRIPT:10, 2 | function |outerFunction|(0)() { 3 | var myFunction = |function(age, name, isAdmin)|(1) { 4 | var result = false; 5 | var value = false; 6 | 7 | if (name === "Sarah") { 8 | if (age < 20 || age > 100) { 9 | if (isAdmin) { 10 | result = true; 11 | value = true; 12 | } 13 | else if (age == 42) { 14 | result = false; 15 | } 16 | else { 17 | result = true; 18 | } 19 | } 20 | } 21 | else if (name == "Gentry" && isAdmin) { 22 | result = false; 23 | } 24 | else { 25 | if (age == 50) { 26 | if (isAdmin) { 27 | if (name == "Amrit") { 28 | result = false; 29 | } 30 | else if (name == "Jane") { 31 | result = true; 32 | } 33 | else { 34 | result = false; 35 | } 36 | } 37 | } 38 | } 39 | 40 | return result; 41 | } 42 | myFunction(42, "Matt", true); 43 | } 44 | 45 | --------------------------------------------------------- 46 | (0): Cyclomatic Complexity Highlight: Property 'outerFunction' has cyclomatic complexity of 1 (10% of threshold) 47 | (1): ReSharper Warning: Element has cyclomatic complexity of 12 (120% of threshold) 48 | 49 | ================ 50 | Thresholds = JAVA_SCRIPT:21, 51 | function |outerFunction|(0)() { 52 | var myFunction = |function(age, name, isAdmin)|(1) { 53 | var result = false; 54 | var value = false; 55 | 56 | if (name === "Sarah") { 57 | if (age < 20 || age > 100) { 58 | if (isAdmin) { 59 | result = true; 60 | value = true; 61 | } 62 | else if (age == 42) { 63 | result = false; 64 | } 65 | else { 66 | result = true; 67 | } 68 | } 69 | } 70 | else if (name == "Gentry" && isAdmin) { 71 | result = false; 72 | } 73 | else { 74 | if (age == 50) { 75 | if (isAdmin) { 76 | if (name == "Amrit") { 77 | result = false; 78 | } 79 | else if (name == "Jane") { 80 | result = true; 81 | } 82 | else { 83 | result = false; 84 | } 85 | } 86 | } 87 | } 88 | 89 | return result; 90 | } 91 | myFunction(42, "Matt", true); 92 | } 93 | 94 | --------------------------------------------------------- 95 | (0): Cyclomatic Complexity Highlight: Property 'outerFunction' has cyclomatic complexity of 1 (4% of threshold) 96 | (1): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (57% of threshold) 97 | 98 | ================ 99 | Thresholds = JAVA_SCRIPT:30, 100 | function |outerFunction|(0)() { 101 | var myFunction = |function(age, name, isAdmin)|(1) { 102 | var result = false; 103 | var value = false; 104 | 105 | if (name === "Sarah") { 106 | if (age < 20 || age > 100) { 107 | if (isAdmin) { 108 | result = true; 109 | value = true; 110 | } 111 | else if (age == 42) { 112 | result = false; 113 | } 114 | else { 115 | result = true; 116 | } 117 | } 118 | } 119 | else if (name == "Gentry" && isAdmin) { 120 | result = false; 121 | } 122 | else { 123 | if (age == 50) { 124 | if (isAdmin) { 125 | if (name == "Amrit") { 126 | result = false; 127 | } 128 | else if (name == "Jane") { 129 | result = true; 130 | } 131 | else { 132 | result = false; 133 | } 134 | } 135 | } 136 | } 137 | 138 | return result; 139 | } 140 | myFunction(42, "Matt", true); 141 | } 142 | 143 | --------------------------------------------------------- 144 | (0): Cyclomatic Complexity Highlight: Property 'outerFunction' has cyclomatic complexity of 1 (3% of threshold) 145 | (1): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (40% of threshold) 146 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/Cpp/MethodWithModifiedSettings.cpp.gold: -------------------------------------------------------------------------------- 1 | Thresholds = CPP:10, 2 | #define true 1 3 | #define false 0 4 | 5 | |int ComplexMethod(int age, char *name, int isAdmin)|(0) 6 | { 7 |     int result = false; 8 | int value = false; 9 |   10 |     if (name == "Sarah") 11 |     { 12 |         if (age < 20 || age > 100) 13 |         { 14 |             if (isAdmin) 15 |             { 16 |                 result = true; 17 | value = true; 18 |             } 19 |             else if (age == 42) 20 |             { 21 |                 result = false; 22 |             } 23 |             else 24 |             { 25 |                 result = true; 26 |             } 27 |         } 28 |     } 29 |     else if (name == "Gentry" && isAdmin) 30 |     { 31 |         result = false; 32 |     } 33 |     else 34 |     { 35 |         if (age == 50) 36 |         { 37 |             if (isAdmin) 38 |             { 39 |                 if (name == "Amrit") 40 |                 { 41 |                     result = false; 42 |                 } 43 |                 else if (name == "Jane") 44 |                 { 45 |                     result = true; 46 |                 } 47 |                 else 48 |                 { 49 |                     result = false; 50 |                 } 51 |             } 52 |         } 53 |     } 54 |   55 |     return result; 56 | } 57 | 58 | --------------------------------------------------------- 59 | (0): ReSharper Warning: Element has cyclomatic complexity of 12 (120% of threshold) 60 | 61 | ================ 62 | Thresholds = CPP:21, 63 | #define true 1 64 | #define false 0 65 | 66 | |int ComplexMethod(int age, char *name, int isAdmin)|(0) 67 | { 68 |     int result = false; 69 | int value = false; 70 |   71 |     if (name == "Sarah") 72 |     { 73 |         if (age < 20 || age > 100) 74 |         { 75 |             if (isAdmin) 76 |             { 77 |                 result = true; 78 | value = true; 79 |             } 80 |             else if (age == 42) 81 |             { 82 |                 result = false; 83 |             } 84 |             else 85 |             { 86 |                 result = true; 87 |             } 88 |         } 89 |     } 90 |     else if (name == "Gentry" && isAdmin) 91 |     { 92 |         result = false; 93 |     } 94 |     else 95 |     { 96 |         if (age == 50) 97 |         { 98 |             if (isAdmin) 99 |             { 100 |                 if (name == "Amrit") 101 |                 { 102 |                     result = false; 103 |                 } 104 |                 else if (name == "Jane") 105 |                 { 106 |                     result = true; 107 |                 } 108 |                 else 109 |                 { 110 |                     result = false; 111 |                 } 112 |             } 113 |         } 114 |     } 115 |   116 |     return result; 117 | } 118 | 119 | --------------------------------------------------------- 120 | (0): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (57% of threshold) 121 | 122 | ================ 123 | Thresholds = CPP:30, 124 | #define true 1 125 | #define false 0 126 | 127 | |int ComplexMethod(int age, char *name, int isAdmin)|(0) 128 | { 129 |     int result = false; 130 | int value = false; 131 |   132 |     if (name == "Sarah") 133 |     { 134 |         if (age < 20 || age > 100) 135 |         { 136 |             if (isAdmin) 137 |             { 138 |                 result = true; 139 | value = true; 140 |             } 141 |             else if (age == 42) 142 |             { 143 |                 result = false; 144 |             } 145 |             else 146 |             { 147 |                 result = true; 148 |             } 149 |         } 150 |     } 151 |     else if (name == "Gentry" && isAdmin) 152 |     { 153 |         result = false; 154 |     } 155 |     else 156 |     { 157 |         if (age == 50) 158 |         { 159 |             if (isAdmin) 160 |             { 161 |                 if (name == "Amrit") 162 |                 { 163 |                     result = false; 164 |                 } 165 |                 else if (name == "Jane") 166 |                 { 167 |                     result = true; 168 |                 } 169 |                 else 170 |                 { 171 |                     result = false; 172 |                 } 173 |             } 174 |         } 175 |     } 176 |   177 |     return result; 178 | } 179 | 180 | --------------------------------------------------------- 181 | (0): Cyclomatic Complexity Highlight: Element has cyclomatic complexity of 12 (40% of threshold) 182 | -------------------------------------------------------------------------------- /runVisualStudio.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | $RootSuffix = "CyclomaticComplexity", 3 | $Version = "9999.0.0" 4 | ) 5 | 6 | Set-StrictMode -Version Latest 7 | $ErrorActionPreference = "Stop" 8 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent 9 | Set-Location $PSScriptRoot 10 | 11 | . ".\settings.ps1" 12 | 13 | $UserProjectXmlFile = "$SourceBasePath\$PluginId\$PluginId.csproj.user" 14 | 15 | if (!(Test-Path "$UserProjectXmlFile")) { 16 | # Get versions from Plugin.props file 17 | $PluginPropsFile = "$SourceBasePath\Plugin.props" 18 | $PluginPropsXml = [xml] (Get-Content "$PluginPropsFile") 19 | $SdkVersionNode = $PluginPropsXml.SelectSingleNode(".//SdkVersion") 20 | $VersionSplit = $SdkVersionNode.InnerText.Split(".") 21 | $MajorVersion = "$($VersionSplit[0]).$($VersionSplit[1])" 22 | 23 | # Determine download link 24 | $ReleaseUrl = "https://data.services.jetbrains.com/products/releases?code=RSU&type=eap&type=release&majorVersion=$MajorVersion" 25 | $VersionEntry = $(Invoke-WebRequest -UseBasicParsing $ReleaseUrl | ConvertFrom-Json).RSU[0] 26 | ## TODO: check versions 27 | $DownloadLink = [uri] ($VersionEntry.downloads.windows.link.replace(".exe", ".Checked.exe")) 28 | 29 | # Download installer 30 | $InstallerFile = "$PSScriptRoot\build\installer\$($DownloadLink.Segments[-1])" 31 | if (!(Test-Path $InstallerFile)) { 32 | mkdir -Force $(Split-Path $InstallerFile -Parent) > $null 33 | Write-Output "Downloading $($DownloadLink.Segments[-2].TrimEnd("/")) installer" 34 | (New-Object System.Net.WebClient).DownloadFile($DownloadLink, $InstallerFile) 35 | } else { 36 | Write-Output "Using cached installer from $InstallerFile" 37 | } 38 | 39 | # Execute installer 40 | Write-Output "Installing experimental hive" 41 | Invoke-Exe $InstallerFile "/VsVersion=$VisualStudioMajorVersion.0" "/SpecificProductNames=ReSharper" "/Hive=$RootSuffix" "/Silent=True" 42 | 43 | $Installations = @(Get-ChildItem "$env:LOCALAPPDATA\JetBrains\ReSharperPlatformVs$VisualStudioMajorVersion\vAny_$VisualStudioInstanceId$RootSuffix\NuGet.Config") 44 | if ($Installations.Count -ne 1) { Write-Error "Found no or multiple installation directories: $Installations" } 45 | $InstallationDirectory = $Installations.Directory 46 | Write-Host "Found installation directory at $InstallationDirectory" 47 | 48 | # Adapt packages.config 49 | if (Test-Path "$InstallationDirectory\packages.config") { 50 | $PackagesXml = [xml] (Get-Content "$InstallationDirectory\packages.config") 51 | } else { 52 | $PackagesXml = [xml] ("") 53 | } 54 | 55 | if ($null -eq $PackagesXml.SelectSingleNode(".//package[@id='$PluginId']/@id")) { 56 | $PluginNode = $PackagesXml.CreateElement('package') 57 | $PluginNode.setAttribute("id", "$PluginId") 58 | $PluginNode.setAttribute("version", "$Version") 59 | 60 | $PackagesNode = $PackagesXml.SelectSingleNode("//packages") 61 | $PackagesNode.AppendChild($PluginNode) > $null 62 | 63 | $PackagesXml.Save("$InstallationDirectory\packages.config") 64 | } 65 | 66 | # Adapt user project file 67 | $HostIdentifier = "$($InstallationDirectory.Parent.Name)_$($InstallationDirectory.Name.Split('_')[-1])" 68 | 69 | Set-Content -Path "$UserProjectXmlFile" -Value "" 70 | 71 | $ProjectXml = [xml] (Get-Content "$UserProjectXmlFile") 72 | $HostIdentifierNode = $ProjectXml.SelectSingleNode(".//HostFullIdentifier") 73 | $HostIdentifierNode.InnerText = $HostIdentifier 74 | $ProjectXml.Save("$UserProjectXmlFile") 75 | 76 | # Install plugin 77 | $PluginRepository = "$env:LOCALAPPDATA\JetBrains\plugins" 78 | Remove-Item "$PluginRepository\${PluginId}.${Version}" -Recurse -ErrorAction Ignore 79 | Invoke-Exe $MSBuildPath "/t:Restore;Rebuild;Pack" "$SolutionPath" "/v:minimal" "/p:PackageVersion=$Version" "/p:PackageOutputPath=`"$OutputDirectory`"" 80 | Invoke-Exe $NuGetPath install $PluginId -OutputDirectory "$PluginRepository" -Source "$OutputDirectory" -DependencyVersion Ignore 81 | 82 | Write-Output "Re-installing experimental hive" 83 | Invoke-Exe "$InstallerFile" "/VsVersion=$VisualStudioMajorVersion.0" "/SpecificProductNames=ReSharper" "/Hive=$RootSuffix" "/Silent=True" 84 | } else { 85 | Write-Warning "Plugin is already installed. To trigger reinstall, delete $UserProjectXmlFile." 86 | } 87 | 88 | Invoke-Exe $MSBuildPath "/t:Restore;Rebuild" "$SolutionPath" "/v:minimal" 89 | Invoke-Exe $DevEnvPath "/rootSuffix $RootSuffix" "/ReSharper.Internal" "/ReSharper.LogFile $PSScriptRoot\ReSharper.log" "/ReSharper.LogLevel Trace" 90 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/ComplexityInfoHighlight.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using JetBrains.DocumentModel; 18 | using JetBrains.ReSharper.Feature.Services.Daemon; 19 | using JetBrains.TextControl.DocumentMarkup; 20 | using Severity = JetBrains.ReSharper.Feature.Services.Daemon.Severity; 21 | #if RIDER 22 | using System.Collections.Generic; 23 | using JetBrains.Application; 24 | using JetBrains.Application.Parts; 25 | using JetBrains.ProjectModel; 26 | using JetBrains.ReSharper.Daemon.CodeInsights; 27 | using JetBrains.ReSharper.Features.SolBuilderDuo.Src; 28 | using JetBrains.ReSharper.Psi.Tree; 29 | using JetBrains.Rider.Backend.Platform.Icons; 30 | using JetBrains.Rider.Model; 31 | using JetBrains.UI.Icons; 32 | #endif 33 | 34 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity 35 | { 36 | #if RESHARPER 37 | [RegisterHighlighter(HighlightAttributeId)] 38 | [StaticSeverityHighlighting(Severity.INFO, typeof(HighlightingGroupIds.CodeSmellStatic), AttributeId = HighlightAttributeId)] 39 | public class ComplexityInfoHighlight : IComplexityHighlighting 40 | { 41 | public const string HighlightAttributeId = "Cyclomatic Complexity Highlight"; 42 | 43 | private readonly DocumentRange range; 44 | 45 | public ComplexityInfoHighlight(string toolTip, DocumentRange range) 46 | { 47 | ToolTip = toolTip; 48 | this.range = range; 49 | } 50 | 51 | public DocumentRange CalculateRange() => range; 52 | public string ToolTip { get; } 53 | public string ErrorStripeToolTip => ToolTip; 54 | public bool IsValid() => true; 55 | } 56 | #else 57 | [ShellComponent(Instantiation.ContainerAsyncPrimaryThread)] 58 | [HighlightingSource] 59 | public class ComplexityCodeInsightsProvider : ICodeInsightsProvider 60 | { 61 | public bool IsAvailableIn(ISolution solution) 62 | { 63 | return true; 64 | } 65 | 66 | public void OnClick(CodeInsightHighlightInfo highlightInfo, ISolution solution, CodeInsightsClickInfo clickInfo) 67 | { 68 | } 69 | 70 | public void OnExtraActionClick(CodeInsightHighlightInfo highlightInfo, string actionId, ISolution solution) 71 | { 72 | } 73 | 74 | public string ProviderId => nameof(ComplexityCodeInsightsProvider); 75 | public string DisplayName => "Cyclomatic Complexity"; 76 | public CodeVisionAnchorKind DefaultAnchor => CodeVisionAnchorKind.Top; 77 | 78 | public ICollection RelativeOrderings => new CodeVisionRelativeOrdering[] 79 | {new CodeVisionRelativeOrderingFirst()}; 80 | } 81 | 82 | [RegisterHighlighter( 83 | HighlightAttributeId, 84 | EffectType = EffectType.NONE, 85 | TransmitUpdates = true, 86 | Layer = HighlighterLayer.SYNTAX + 1, 87 | GroupId = HighlighterGroupIds.HIDDEN)] 88 | [StaticSeverityHighlighting(Severity.INFO, typeof(HighlightingGroupIds.CodeInsights), AttributeId = HighlightAttributeId)] 89 | public class ComplexityCodeInsightsHighlight : CodeInsightsHighlighting 90 | { 91 | public const string HighlightAttributeId = "Cyclomatic Complexity Code Insight Highlight"; 92 | 93 | private const int c_warningThreshold = 80; 94 | 95 | private static string GetLensText(int percentage) 96 | => (percentage < c_warningThreshold 97 | ? "simple enough" 98 | : percentage <= 100 99 | ? "mildly complex" 100 | : percentage <= 200 101 | ? "very complex" 102 | : "refactor me?!") + $" ({percentage}%)"; 103 | 104 | private static IconId GetIconId(int percentage) 105 | => percentage < c_warningThreshold 106 | ? SolBuilderDuoThemedIcons.SolBuilderDuoRunningBuild.Id 107 | : percentage <= 100 108 | ? SolBuilderDuoThemedIcons.SolBuilderDuoRunningBuildWarning.Id 109 | : SolBuilderDuoThemedIcons.SolBuilderDuoRunningBuildError.Id; 110 | 111 | private static string GetMoreText(int complexity, int percentage) 112 | => $"Cyclomatic complexity of {complexity} ({percentage}% of threshold)"; 113 | 114 | public ComplexityCodeInsightsHighlight( 115 | ITypeMemberDeclaration declaration, 116 | int complexity, 117 | int percentage, 118 | ICodeInsightsProvider provider, 119 | IconHost iconHost) 120 | : base( 121 | declaration.GetNameDocumentRange(), 122 | GetLensText(percentage), 123 | GetMoreText(complexity, percentage), 124 | GetMoreText(complexity, percentage), 125 | provider, 126 | declaration.DeclaredElement, 127 | iconHost.Transform(GetIconId(percentage))) 128 | { 129 | } 130 | } 131 | #endif 132 | } 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![official JetBrains project](http://jb.gg/badges/official-flat-square.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) 2 | 3 | [![Rider](https://img.shields.io/jetbrains/plugin/v/10395-cyclomatic-complexity.svg?label=rider%20&colorB=0A7BBB&style=flat-square&logo=%20data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAADAAAAAsCAYAAAAjFjtnAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAEEklEQVRoQ%2B2ZS0hUURjHLSoKominVIta1aLaFYhBES1t1bLFRCAREbQKaiHpqgeEpjDjRhIFHWwzIyo6oWXqwmfkW0evr9TxNT7GcZxX%2FzPzzZk53jsyc%2B91I%2FcHB4a5%2F%2B%2B79z%2Fn3O98RzOOBKFQ6BeGlOrw%2B%2F2S0%2Bn83dXV9a6srOwSpUlKfX19yvnn5%2BelwcFBW09Pz8uamporlOJgEOgMq2R7ezuAm33Cx%2BOUTgYMqMq%2Ft7cX7O3t7QbXKJUyWgzEWFhYaMONTlJKAbUGYvh8vjBymy0Wi2J%2BXQwwxsbGvlFKAa0GGHjG8Nra2nd8lM%2B0kgGPx9OK76sSx9LSkgNrfyMQCOySTAAxodLS0uuUlqNkAPnG9%2BdfWVlxLC8vs%2FxBkskYHx%2B3Udo4CJbdoKqqKpcuy8DLmzU3N%2FeTpAJTU1MfSMZRMoBCYKHLMhwOx12YacBzkVpkeHjYRNIo6RpgsPXodrtdJOdgvdpJwknXQIyRkZE3mA2KiIP7LqJCnSGZOgMM3MBOcg5y6WaAsb6%2B%2Fp5CBDo7O5%2BSRL0BTOWhG2hpaTnt9Xr%2FURhndna2lSTqDWxtbUkk50xOTupqgDE9PV1AYZzV1VVXXl5etKyma6C8vDwTJa2GpALY1J6QjKPVAGY6l8I4mJVwR0dH9D1QMtDW1ubCjaXE0dDQIA0MDEj45f0kE0DlmMeUn4gkTUCrgYqKCpkBxszMTHID6YJq4cVU50QS7kOrAZvNJjOAZ9bPAHoWT3Nz84tIMgW0GsC%2B84rCOFjCYV5K1RpgPQo2tKbi4uLbkURJ0GoALUo3hXHQRP7BpWMRgZIBrGcPpmgjNjY3N2XtA16knb6%2BvouRJAegxUB1dXVuMCjvLCYmJspIomwA3wlVqL29PXt3d1e2t6MeD%2BTn58te3ETUGkDByMTyHKMQDmL9drv9FslSM8DAzvuRLgtgjX4miSJqDOCdugfNKMkFJElqIlmUVA0w0PdPkISDmQk3NjZmk0SGkgF0tq2VlZWm2ECpNFmtVtPi4uJzLN8fuD8pRdBa%2BIaGhrIodZR0DKCk5WBafSTj4IEmMeWnSCagZEANmBEPVsEDShsnHQOM%2Fv7%2BLyQTQPKvJBHQwwCqjhet%2Bn1KKZKuAfQg51GH%2F5KUw5bS6OjoHZJxtBjAr876q14ssxuUTk66Bhg4dNzECYzUcbAv7KCPukCyCGoMIPcaGjYrK6NIEa33ycBb%2FRhtgClx4EEu0%2BWkoPo83B%2FHBo59QqzZbH4LE7UHjbq6ulpsjM9QJEwFBQWPlHoqAwMDAwMDAwODo0hJScnZoqKic3oOHPST%2Fs9Md1wulxMjrNfAuZYdbK5S%2BsNH6UCjBfZ3HJgwDKQMDLzGKNRrwECh2%2B0WjpUGimRk%2FAdgThdOY4UJ9QAAAABJRU5ErkJggg%3D%3D)](https://plugins.jetbrains.com/plugin/10395-cyclomatic-complexity) 4 | [![ReSharper](https://img.shields.io/resharper/v/PowerToys.CyclomaticComplexity.svg?label=resharper%20&colorB=0A7BBB&style=flat-square&logo=%20data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAADIAAAAxCAYAAACYq%2FofAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAE%2BElEQVRoQ%2B2Ya0hkZRzG3d1oWehLLQS2QRC1tEWflhLqU0QgsbAb6ZesiL5KXxKE3GBa86uE0IdWiTJq8BLGMiqapnjDYfAy3jCv6XgfZC7explxnJ73zDPvnOM7M%2BtlslrOD%2F7InOc5%2F%2Ff8z3mvZplkgJKSkuzS0tIb8bBYLC9RMoDr1%2FW%2B4uLiG5RUDg8PFxCek4Tf7%2Fe4XK7W0dHRosbGxieZ6tg4nU7%2F3t5eNB67u7tBSpK8vLwr29vbXr1veno6Ci7QYgSCT6inJRQK7UxNTd3F23ucKR%2FGBbyMAG%2FXWFpaClCT2Gy2F4PBIB0x0FYbZRXoZyokzszMTFdtbe0Vpk1JTU3NawcHB7wrBn7XU5a0tLS8Q1kyNDRko6wCPSOFCCYnJ2uYNiXLy8tv0y4ZHx%2F%2FnrIEL6aIsmRlZeUzyirQM1YI3mxodXX1OaZOCvr517RLPB7PR5QlGH9WyhIM%2BDzKKtCVQtCHf8Cfi0ejsLDwGfTdj2dnZ23hcPgQ1xRQSAVTayDXbYQ1Hmtra7u0SlDc4djYWEQfW1tbVBNMTExoWldX1wzTJ4CetBDKKbHb7Rb4eEeCjY2NOVo04PmCUsbAyzC0oYHrpyokNzf3Mvp7mLdIcO%2F%2FqxAButdPvEVyHoWg3cwWgi8yy1skSQr5HOEXEYlEDOuHAIteAN3Rrw8Qoizx%2BXzbcd3tdjuZPoHwxKwJjlNIX1%2FfG3gwZZBgsI%2FSooAuoXyd9vb2IsoSDOp6yhp4nmggEHiBcnLgO1EhdXV1l3p7ewuwrdik3QC%2B0pe0KmBK7aFNQzzg8PDwNcoSfJE%2FaNHA9iRUWVn5LOXkwKcUsrm5GcEqGjwaIyMjwZ2dnaB4gGSguHB1dfV1pjaQn59%2Fyev1TtCqgd%2FRqqqqp2iRrK%2Bv0xFjf39%2FlVJq4FMKOQ3oZtH%2B%2Fv57TKsgti94QXTHwKCdpCzBzvgaZQm%2Bsp1yauA7cyFic4civmXKpGBVfpV2CRZCB2XJ4OBgLmUJvtx3lFMD35kKwf5nrbOz8w5SKdtrdMGfEZsi8Pa3eYsE1%2Ffiui6UJV3MdnF9YWHhPtMbge%2FUhSwuLkYKCgqymUoBDT%2BgNWOgkF%2BY3gg0pRDMEn%2FOzc3d1we6zwZlA5gqq5lK4V8vBA%2BgTL%2BYJt%2FD9QgtEnSZcFtb2y3aDPwnCxHMz88bFqo4WHVXm5ubL9MmQZ4yRAdW7yFaJVjgQkLTB44BDsoSjA%2B33oMZ7C7TG4H32IWUlZVlo9st0WYAx9UfaVMoLy9%2FCznpjIFV%2FnfKku7u7luUJVi7fqWcHniPXYgAjd1Bb6IzgVhHcFb5lDYD2La8S5sEC6zSBh76K8oSLMCfUE4PvCcqxGKxXMTgV05vAhyGvHj7ypYDW5NvaJFg2v6AsgRH3t8oSyoqKm5TTg%2B8JypE0NDQcBVvWVkXBCiylTYJthx1lCXYmrxJWYKd7V%2BUNfhflMcopwfGExciwEz1figUEud0Q4hrDofjQ9o0XC6XT%2B%2FBnixKSYJj9FVMGjt6Hwa2W%2BzRaEkPnvsmHjxHH7j2POW04Mu83tHRkXM0rFbrK7Ro9PT03NTr9fX1OZQk4v9ira2thjzY4r9M2cTExMTExMTExMTkEaSpqemB3W5fznTg2LrMJs6HgYEB5YSYKdjE%2BfDIFOJ0Ol1sN%2BOwifMB7T2Bc%2FrT%2F0SwCZOHk5X1N%2Fu%2FydjP06PFAAAAAElFTkSuQmCC)](https://resharper-plugins.jetbrains.com/packages/PowerToys.CyclomaticComplexity/) 5 | 6 | # Cyclomatic complexity plugin for ReSharper 7 | 8 | This is a ReSharper plugin that measures [cyclomatic complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity) as you type. If the complexity of a method, function or property exceeds a configurable threshold, the method, function or property is marked with a warning highlight ("squiggly"). Supported languages are C#, VB, JavaScript, TypeScript and C++. 9 | 10 | It can be installed by going to **ReSharper → Extensions Manager** and searching for "Cyclomatic Complexity". 11 | 12 | Cyclomatic complexity is a metric intended to show the complexity of your code. It measures the number of paths through a code block - the more branches and conditional statements, the higher the metric. As the code gets more complex, the metric gets higher. A higher value is a good indicator that a method is getting too complex, and is becoming a maintainability risk, and should be refactored. You can read more about [cyclomatic complexity at wikipedia](https://en.wikipedia.org/wiki/Cyclomatic_complexity). 13 | 14 | The complexity of a method, function or other code member is always available as a tooltip on the method name: 15 | 16 | ![Complexity displayed as an info tooltip](docs/info.png) 17 | 18 | When the complexity reaches a configurable threshold, the tooltip is shown as a warning: 19 | 20 | ![Complexity exceeds the configurable threshold](docs/warning.png) 21 | 22 | And the complexity threshold can be configured in the options, per language: 23 | 24 | ![Options](docs/options.png) 25 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem GRADLE JVM WRAPPER START MARKER 40 | 41 | setlocal 42 | set BUILD_DIR=%LOCALAPPDATA%\gradle-jvm 43 | set JVM_TARGET_DIR=%BUILD_DIR%\jdk-17.0.3.1_windows-x64_bin-d6ede5\ 44 | 45 | set JVM_URL=https://download.oracle.com/java/17/archive/jdk-17.0.3.1_windows-x64_bin.zip 46 | 47 | set IS_TAR_GZ=0 48 | set JVM_TEMP_FILE=gradle-jvm.zip 49 | 50 | if /I "%JVM_URL:~-7%"==".tar.gz" ( 51 | set IS_TAR_GZ=1 52 | set JVM_TEMP_FILE=gradle-jvm.tar.gz 53 | ) 54 | 55 | set POWERSHELL=%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe 56 | 57 | if not exist "%JVM_TARGET_DIR%" MD "%JVM_TARGET_DIR%" 58 | 59 | if not exist "%JVM_TARGET_DIR%.flag" goto downloadAndExtractJvm 60 | 61 | set /p CURRENT_FLAG=<"%JVM_TARGET_DIR%.flag" 62 | if "%CURRENT_FLAG%" == "%JVM_URL%" goto continueWithJvm 63 | 64 | :downloadAndExtractJvm 65 | 66 | PUSHD "%BUILD_DIR%" 67 | if errorlevel 1 goto fail 68 | 69 | echo Downloading %JVM_URL% to %BUILD_DIR%\%JVM_TEMP_FILE% 70 | if exist "%JVM_TEMP_FILE%" DEL /F "%JVM_TEMP_FILE%" 71 | "%POWERSHELL%" -nologo -noprofile -Command "Set-StrictMode -Version 3.0; $ErrorActionPreference = \"Stop\"; (New-Object Net.WebClient).DownloadFile('%JVM_URL%', '%JVM_TEMP_FILE%')" 72 | if errorlevel 1 goto fail 73 | 74 | POPD 75 | 76 | RMDIR /S /Q "%JVM_TARGET_DIR%" 77 | if errorlevel 1 goto fail 78 | 79 | MKDIR "%JVM_TARGET_DIR%" 80 | if errorlevel 1 goto fail 81 | 82 | PUSHD "%JVM_TARGET_DIR%" 83 | if errorlevel 1 goto fail 84 | 85 | echo Extracting %BUILD_DIR%\%JVM_TEMP_FILE% to %JVM_TARGET_DIR% 86 | 87 | if "%IS_TAR_GZ%"=="1" ( 88 | tar xf "..\\%JVM_TEMP_FILE%" 89 | ) else ( 90 | "%POWERSHELL%" -nologo -noprofile -command "Set-StrictMode -Version 3.0; $ErrorActionPreference = \"Stop\"; Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::ExtractToDirectory('..\\%JVM_TEMP_FILE%', '.');" 91 | ) 92 | if errorlevel 1 goto fail 93 | 94 | DEL /F "..\%JVM_TEMP_FILE%" 95 | if errorlevel 1 goto fail 96 | 97 | POPD 98 | 99 | echo %JVM_URL%>"%JVM_TARGET_DIR%.flag" 100 | if errorlevel 1 goto fail 101 | 102 | :continueWithJvm 103 | 104 | set JAVA_HOME= 105 | for /d %%d in ("%JVM_TARGET_DIR%"*) do if exist "%%d\bin\java.exe" set JAVA_HOME=%%d 106 | if not exist "%JAVA_HOME%\bin\java.exe" ( 107 | echo Unable to find java.exe under %JVM_TARGET_DIR% 108 | goto fail 109 | ) 110 | 111 | endlocal & set JAVA_HOME=%JAVA_HOME% 112 | 113 | @rem GRADLE JVM WRAPPER END MARKER 114 | 115 | @rem Find java.exe 116 | if defined JAVA_HOME goto findJavaFromJavaHome 117 | 118 | set JAVA_EXE=java.exe 119 | %JAVA_EXE% -version >NUL 2>&1 120 | if %ERRORLEVEL% equ 0 goto execute 121 | 122 | echo. 1>&2 123 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 124 | echo. 1>&2 125 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 126 | echo location of your Java installation. 1>&2 127 | 128 | goto fail 129 | 130 | :findJavaFromJavaHome 131 | set JAVA_HOME=%JAVA_HOME:"=% 132 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 133 | 134 | if exist "%JAVA_EXE%" goto execute 135 | 136 | echo. 1>&2 137 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 138 | echo. 1>&2 139 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 140 | echo location of your Java installation. 1>&2 141 | 142 | goto fail 143 | 144 | :execute 145 | @rem Setup the command line 146 | 147 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 148 | 149 | 150 | @rem Execute Gradle 151 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 152 | 153 | :end 154 | @rem End local scope for the variables with windows NT shell 155 | if %ERRORLEVEL% equ 0 goto mainEnd 156 | 157 | :fail 158 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 159 | rem the _cmd.exe /c_ return code! 160 | set EXIT_CODE=%ERRORLEVEL% 161 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 162 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 163 | exit /b %EXIT_CODE% 164 | 165 | :mainEnd 166 | if "%OS%"=="Windows_NT" endlocal 167 | 168 | :omega 169 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/CSharp/MethodBodyTooComplexForValueAnalysisAndHighCyclomaticComplexity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class MethodBodyTooComplex 4 | { 5 | // This relies on an implementation detail of ReSharper's value analysis 6 | // It only tracks 84 variables in a method (any more starts to incur too 7 | // much cost). A warning is added that is normally set to "Do not show", 8 | // but since we're concerned with complexity, I think it's worth enabling 9 | // Since we get two warnings on the same method declaration, ReSharper 10 | // handles the overlap by hiding the cyclomatic complexity warning 11 | public static void MethodBodyTooComplexForValueAnalysisAndHighCyclomaticComplexity(object obj) 12 | { 13 | var t1 = obj as string; 14 | if (t1 != null) { } 15 | var t2 = obj as string; 16 | if (t2 != null) { } 17 | var t3 = obj as string; 18 | if (t3 != null) { } 19 | var t4 = obj as string; 20 | if (t4 != null) { } 21 | var t5 = obj as string; 22 | if (t5 != null) { } 23 | var t6 = obj as string; 24 | if (t6 != null) { } 25 | var t7 = obj as string; 26 | if (t7 != null) { } 27 | var t8 = obj as string; 28 | if (t8 != null) { } 29 | var t9 = obj as string; 30 | if (t9 != null) { } 31 | var t10 = obj as string; 32 | if (t10 != null) { } 33 | var t11 = obj as string; 34 | if (t11 != null) { } 35 | var t12 = obj as string; 36 | if (t12 != null) { } 37 | var t13 = obj as string; 38 | if (t13 != null) { } 39 | var t14 = obj as string; 40 | if (t14 != null) { } 41 | var t15 = obj as string; 42 | if (t15 != null) { } 43 | var t16 = obj as string; 44 | if (t16 != null) { } 45 | var t17 = obj as string; 46 | if (t17 != null) { } 47 | var t18 = obj as string; 48 | if (t18 != null) { } 49 | var t19 = obj as string; 50 | if (t19 != null) { } 51 | var t20 = obj as string; 52 | if (t20 != null) { } 53 | var t21 = obj as string; 54 | if (t21 != null) { } 55 | var t22 = obj as string; 56 | if (t22 != null) { } 57 | var t23 = obj as string; 58 | if (t23 != null) { } 59 | var t24 = obj as string; 60 | if (t24 != null) { } 61 | var t25 = obj as string; 62 | if (t25 != null) { } 63 | var t26 = obj as string; 64 | if (t26 != null) { } 65 | var t27 = obj as string; 66 | if (t27 != null) { } 67 | var t28 = obj as string; 68 | if (t28 != null) { } 69 | var t29 = obj as string; 70 | if (t29 != null) { } 71 | var t30 = obj as string; 72 | if (t30 != null) { } 73 | var t31 = obj as string; 74 | if (t31 != null) { } 75 | var t32 = obj as string; 76 | if (t32 != null) { } 77 | var t33 = obj as string; 78 | if (t33 != null) { } 79 | var t34 = obj as string; 80 | if (t34 != null) { } 81 | var t35 = obj as string; 82 | if (t35 != null) { } 83 | var t36 = obj as string; 84 | if (t36 != null) { } 85 | var t37 = obj as string; 86 | if (t37 != null) { } 87 | var t38 = obj as string; 88 | if (t38 != null) { } 89 | var t39 = obj as string; 90 | if (t39 != null) { } 91 | var t40 = obj as string; 92 | if (t40 != null) { } 93 | var t41 = obj as string; 94 | if (t41 != null) { } 95 | var t42 = obj as string; 96 | if (t42 != null) { } 97 | var t43 = obj as string; 98 | if (t43 != null) { } 99 | var t44 = obj as string; 100 | if (t44 != null) { } 101 | var t45 = obj as string; 102 | if (t45 != null) { } 103 | var t46 = obj as string; 104 | if (t46 != null) { } 105 | var t47 = obj as string; 106 | if (t47 != null) { } 107 | var t48 = obj as string; 108 | if (t48 != null) { } 109 | var t49 = obj as string; 110 | if (t49 != null) { } 111 | var t50 = obj as string; 112 | if (t50 != null) { } 113 | var t51 = obj as string; 114 | if (t51 != null) { } 115 | var t52 = obj as string; 116 | if (t52 != null) { } 117 | var t53 = obj as string; 118 | if (t53 != null) { } 119 | var t54 = obj as string; 120 | if (t54 != null) { } 121 | var t55 = obj as string; 122 | if (t55 != null) { } 123 | var t56 = obj as string; 124 | if (t56 != null) { } 125 | var t57 = obj as string; 126 | if (t57 != null) { } 127 | var t58 = obj as string; 128 | if (t58 != null) { } 129 | var t59 = obj as string; 130 | if (t59 != null) { } 131 | var t60 = obj as string; 132 | if (t60 != null) { } 133 | var t61 = obj as string; 134 | if (t61 != null) { } 135 | var t62 = obj as string; 136 | if (t62 != null) { } 137 | var t63 = obj as string; 138 | if (t63 != null) { } 139 | var t64 = obj as string; 140 | if (t64 != null) { } 141 | var t65 = obj as string; 142 | if (t65 != null) { } 143 | var t66 = obj as string; 144 | if (t66 != null) { } 145 | var t67 = obj as string; 146 | if (t67 != null) { } 147 | var t68 = obj as string; 148 | if (t68 != null) { } 149 | var t69 = obj as string; 150 | if (t69 != null) { } 151 | var t70 = obj as string; 152 | if (t70 != null) { } 153 | var t71 = obj as string; 154 | if (t71 != null) { } 155 | var t72 = obj as string; 156 | if (t72 != null) { } 157 | var t73 = obj as string; 158 | if (t73 != null) { } 159 | var t74 = obj as string; 160 | if (t74 != null) { } 161 | var t75 = obj as string; 162 | if (t75 != null) { } 163 | var t76 = obj as string; 164 | if (t76 != null) { } 165 | var t77 = obj as string; 166 | if (t77 != null) { } 167 | var t78 = obj as string; 168 | if (t78 != null) { } 169 | var t79 = obj as string; 170 | if (t79 != null) { } 171 | var t80 = obj as string; 172 | if (t80 != null) { } 173 | var t81 = obj as string; 174 | if (t81 != null) { } 175 | var t82 = obj as string; 176 | if (t82 != null) { } 177 | var t83 = obj as string; 178 | if (t83 != null) { } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/CSharp/MethodBodyTooComplexForValueAnalysisAndHighCyclomaticComplexity.cs.gold: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class |MethodBodyTooComplex|(0) 4 | { 5 | // This relies on an implementation detail of ReSharper's value analysis 6 | // It only tracks 84 variables in a method (any more starts to incur too 7 | // much cost). A warning is added that is normally set to "Do not show", 8 | // but since we're concerned with complexity, I think it's worth enabling 9 | // Since we get two warnings on the same method declaration, ReSharper 10 | // handles the overlap by hiding the cyclomatic complexity warning 11 | public static void |MethodBodyTooComplexForValueAnalysisAndHighCyclomaticComplexity|(1)(object obj) 12 | { 13 | var t1 = obj as string; 14 | if (t1 != null) { } 15 | var t2 = obj as string; 16 | if (t2 != null) { } 17 | var t3 = obj as string; 18 | if (t3 != null) { } 19 | var t4 = obj as string; 20 | if (t4 != null) { } 21 | var t5 = obj as string; 22 | if (t5 != null) { } 23 | var t6 = obj as string; 24 | if (t6 != null) { } 25 | var t7 = obj as string; 26 | if (t7 != null) { } 27 | var t8 = obj as string; 28 | if (t8 != null) { } 29 | var t9 = obj as string; 30 | if (t9 != null) { } 31 | var t10 = obj as string; 32 | if (t10 != null) { } 33 | var t11 = obj as string; 34 | if (t11 != null) { } 35 | var t12 = obj as string; 36 | if (t12 != null) { } 37 | var t13 = obj as string; 38 | if (t13 != null) { } 39 | var t14 = obj as string; 40 | if (t14 != null) { } 41 | var t15 = obj as string; 42 | if (t15 != null) { } 43 | var t16 = obj as string; 44 | if (t16 != null) { } 45 | var t17 = obj as string; 46 | if (t17 != null) { } 47 | var t18 = obj as string; 48 | if (t18 != null) { } 49 | var t19 = obj as string; 50 | if (t19 != null) { } 51 | var t20 = obj as string; 52 | if (t20 != null) { } 53 | var t21 = obj as string; 54 | if (t21 != null) { } 55 | var t22 = obj as string; 56 | if (t22 != null) { } 57 | var t23 = obj as string; 58 | if (t23 != null) { } 59 | var t24 = obj as string; 60 | if (t24 != null) { } 61 | var t25 = obj as string; 62 | if (t25 != null) { } 63 | var t26 = obj as string; 64 | if (t26 != null) { } 65 | var t27 = obj as string; 66 | if (t27 != null) { } 67 | var t28 = obj as string; 68 | if (t28 != null) { } 69 | var t29 = obj as string; 70 | if (t29 != null) { } 71 | var t30 = obj as string; 72 | if (t30 != null) { } 73 | var t31 = obj as string; 74 | if (t31 != null) { } 75 | var t32 = obj as string; 76 | if (t32 != null) { } 77 | var t33 = obj as string; 78 | if (t33 != null) { } 79 | var t34 = obj as string; 80 | if (t34 != null) { } 81 | var t35 = obj as string; 82 | if (t35 != null) { } 83 | var t36 = obj as string; 84 | if (t36 != null) { } 85 | var t37 = obj as string; 86 | if (t37 != null) { } 87 | var t38 = obj as string; 88 | if (t38 != null) { } 89 | var t39 = obj as string; 90 | if (t39 != null) { } 91 | var t40 = obj as string; 92 | if (t40 != null) { } 93 | var t41 = obj as string; 94 | if (t41 != null) { } 95 | var t42 = obj as string; 96 | if (t42 != null) { } 97 | var t43 = obj as string; 98 | if (t43 != null) { } 99 | var t44 = obj as string; 100 | if (t44 != null) { } 101 | var t45 = obj as string; 102 | if (t45 != null) { } 103 | var t46 = obj as string; 104 | if (t46 != null) { } 105 | var t47 = obj as string; 106 | if (t47 != null) { } 107 | var t48 = obj as string; 108 | if (t48 != null) { } 109 | var t49 = obj as string; 110 | if (t49 != null) { } 111 | var t50 = obj as string; 112 | if (t50 != null) { } 113 | var t51 = obj as string; 114 | if (t51 != null) { } 115 | var t52 = obj as string; 116 | if (t52 != null) { } 117 | var t53 = obj as string; 118 | if (t53 != null) { } 119 | var t54 = obj as string; 120 | if (t54 != null) { } 121 | var t55 = obj as string; 122 | if (t55 != null) { } 123 | var t56 = obj as string; 124 | if (t56 != null) { } 125 | var t57 = obj as string; 126 | if (t57 != null) { } 127 | var t58 = obj as string; 128 | if (t58 != null) { } 129 | var t59 = obj as string; 130 | if (t59 != null) { } 131 | var t60 = obj as string; 132 | if (t60 != null) { } 133 | var t61 = obj as string; 134 | if (t61 != null) { } 135 | var t62 = obj as string; 136 | if (t62 != null) { } 137 | var t63 = obj as string; 138 | if (t63 != null) { } 139 | var t64 = obj as string; 140 | if (t64 != null) { } 141 | var t65 = obj as string; 142 | if (t65 != null) { } 143 | var t66 = obj as string; 144 | if (t66 != null) { } 145 | var t67 = obj as string; 146 | if (t67 != null) { } 147 | var t68 = obj as string; 148 | if (t68 != null) { } 149 | var t69 = obj as string; 150 | if (t69 != null) { } 151 | var t70 = obj as string; 152 | if (t70 != null) { } 153 | var t71 = obj as string; 154 | if (t71 != null) { } 155 | var t72 = obj as string; 156 | if (t72 != null) { } 157 | var t73 = obj as string; 158 | if (t73 != null) { } 159 | var t74 = obj as string; 160 | if (t74 != null) { } 161 | var t75 = obj as string; 162 | if (t75 != null) { } 163 | var t76 = obj as string; 164 | if (t76 != null) { } 165 | var t77 = obj as string; 166 | if (t77 != null) { } 167 | var t78 = obj as string; 168 | if (t78 != null) { } 169 | var t79 = obj as string; 170 | if (t79 != null) { } 171 | var t80 = obj as string; 172 | if (t80 != null) { } 173 | var t81 = obj as string; 174 | if (t81 != null) { } 175 | var t82 = obj as string; 176 | if (t82 != null) { } 177 | var t83 = obj as string; 178 | if (t83 != null) { } 179 | } 180 | } 181 | 182 | --------------------------------------------------------- 183 | (0): Cyclomatic Complexity Highlight: Class 'MethodBodyTooComplex' has cyclomatic complexity of 1 (5% of threshold) 184 | (1): ReSharper Warning: Method 'MethodBodyTooComplexForValueAnalysisAndHighCyclomaticComplexity' has cyclomatic complexity of 84 (420% of threshold) 185 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity.Tests/test/data/CSharp/ComplexMethodWithModifiedSettings.cs.gold: -------------------------------------------------------------------------------- 1 | Thresholds = CSHARP:10, 2 | using System; 3 | 4 | class |ComplexityExample|(0) 5 | { 6 | // From http://dontcodetired.com/blog/post/Using-Cyclomatic-Complexity-as-an-Indicator-of-Clean-Code.aspx 7 | // Used by permission :) https://twitter.com/robertsjason/status/618041977997627392 8 | public bool |SomeComplexMethod|(1)(int age, string name, bool isAdmin) 9 | { 10 |     bool result = false; 11 | bool value = false; 12 |   13 |     if (name == "Sarah") 14 |     { 15 |         if (age < 20 || age > 100) 16 |         { 17 |             if (isAdmin) 18 |             { 19 |                 result = true; 20 | value = true; 21 |             } 22 |             else if (age == 42) 23 |             { 24 |                 result = false; 25 |             } 26 |             else 27 |             { 28 |                 result = true; 29 |             } 30 |         } 31 |     } 32 |     else if (name == "Gentry" && isAdmin) 33 |     { 34 |         result = false; 35 |     } 36 |     else 37 |     { 38 |         if (age == 50) 39 |         { 40 |             if (isAdmin) 41 |             { 42 |                 if (name == "Amrit") 43 |                 { 44 |                     result = false; 45 |                 } 46 |                 else if (name == "Jane") 47 |                 { 48 |                     result = true; 49 |                 } 50 |                 else 51 |                 { 52 |                     result = false; 53 |                 } 54 |             } 55 |         } 56 |     } 57 |   58 |     return result; 59 | } 60 | } 61 | 62 | --------------------------------------------------------- 63 | (0): Cyclomatic Complexity Highlight: Class 'ComplexityExample' has cyclomatic complexity of 1 (10% of threshold) 64 | (1): ReSharper Warning: Method 'SomeComplexMethod' has cyclomatic complexity of 12 (120% of threshold) 65 | 66 | ================ 67 | Thresholds = CSHARP:21, 68 | using System; 69 | 70 | class |ComplexityExample|(0) 71 | { 72 | // From http://dontcodetired.com/blog/post/Using-Cyclomatic-Complexity-as-an-Indicator-of-Clean-Code.aspx 73 | // Used by permission :) https://twitter.com/robertsjason/status/618041977997627392 74 | public bool |SomeComplexMethod|(1)(int age, string name, bool isAdmin) 75 | { 76 |     bool result = false; 77 | bool value = false; 78 |   79 |     if (name == "Sarah") 80 |     { 81 |         if (age < 20 || age > 100) 82 |         { 83 |             if (isAdmin) 84 |             { 85 |                 result = true; 86 | value = true; 87 |             } 88 |             else if (age == 42) 89 |             { 90 |                 result = false; 91 |             } 92 |             else 93 |             { 94 |                 result = true; 95 |             } 96 |         } 97 |     } 98 |     else if (name == "Gentry" && isAdmin) 99 |     { 100 |         result = false; 101 |     } 102 |     else 103 |     { 104 |         if (age == 50) 105 |         { 106 |             if (isAdmin) 107 |             { 108 |                 if (name == "Amrit") 109 |                 { 110 |                     result = false; 111 |                 } 112 |                 else if (name == "Jane") 113 |                 { 114 |                     result = true; 115 |                 } 116 |                 else 117 |                 { 118 |                     result = false; 119 |                 } 120 |             } 121 |         } 122 |     } 123 |   124 |     return result; 125 | } 126 | } 127 | 128 | --------------------------------------------------------- 129 | (0): Cyclomatic Complexity Highlight: Class 'ComplexityExample' has cyclomatic complexity of 1 (4% of threshold) 130 | (1): Cyclomatic Complexity Highlight: Method 'SomeComplexMethod' has cyclomatic complexity of 12 (57% of threshold) 131 | 132 | ================ 133 | Thresholds = CSHARP:30, 134 | using System; 135 | 136 | class |ComplexityExample|(0) 137 | { 138 | // From http://dontcodetired.com/blog/post/Using-Cyclomatic-Complexity-as-an-Indicator-of-Clean-Code.aspx 139 | // Used by permission :) https://twitter.com/robertsjason/status/618041977997627392 140 | public bool |SomeComplexMethod|(1)(int age, string name, bool isAdmin) 141 | { 142 |     bool result = false; 143 | bool value = false; 144 |   145 |     if (name == "Sarah") 146 |     { 147 |         if (age < 20 || age > 100) 148 |         { 149 |             if (isAdmin) 150 |             { 151 |                 result = true; 152 | value = true; 153 |             } 154 |             else if (age == 42) 155 |             { 156 |                 result = false; 157 |             } 158 |             else 159 |             { 160 |                 result = true; 161 |             } 162 |         } 163 |     } 164 |     else if (name == "Gentry" && isAdmin) 165 |     { 166 |         result = false; 167 |     } 168 |     else 169 |     { 170 |         if (age == 50) 171 |         { 172 |             if (isAdmin) 173 |             { 174 |                 if (name == "Amrit") 175 |                 { 176 |                     result = false; 177 |                 } 178 |                 else if (name == "Jane") 179 |                 { 180 |                     result = true; 181 |                 } 182 |                 else 183 |                 { 184 |                     result = false; 185 |                 } 186 |             } 187 |         } 188 |     } 189 |   190 |     return result; 191 | } 192 | } 193 | 194 | --------------------------------------------------------- 195 | (0): Cyclomatic Complexity Highlight: Class 'ComplexityExample' has cyclomatic complexity of 1 (3% of threshold) 196 | (1): Cyclomatic Complexity Highlight: Method 'SomeComplexMethod' has cyclomatic complexity of 12 (40% of threshold) 197 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/CyclomaticComplexityIcons/ThemedIcons.CyclomaticComplexity.Generated.Xaml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/CyclomaticComplexityIcons/CyclomaticComplexity.xaml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | -------------------------------------------------------------------------------- /src/dotnet/PowerToys.CyclomaticComplexity/ComplexityAnalysisElementProblemAnalyzer.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-2015 JetBrains 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using System.Collections.Generic; 18 | using System.Linq; 19 | using JetBrains.Application.Settings; 20 | using JetBrains.DocumentModel; 21 | using JetBrains.ReSharper.Feature.Services.Daemon; 22 | using JetBrains.ReSharper.Psi; 23 | using JetBrains.ReSharper.Psi.ControlFlow; 24 | using JetBrains.ReSharper.Psi.CSharp.Tree; 25 | using JetBrains.ReSharper.Psi.JavaScript.Tree; 26 | using JetBrains.ReSharper.Psi.Tree; 27 | using JetBrains.UI.RichText; 28 | using JetBrains.Util; 29 | #if RIDER 30 | using JetBrains.Rider.Backend.Platform.Icons; 31 | #endif 32 | 33 | namespace JetBrains.ReSharper.Plugins.CyclomaticComplexity 34 | { 35 | [ElementProblemAnalyzer(typeof(ITreeNode))] 36 | public class ComplexityAnalysisElementProblemAnalyzer : ElementProblemAnalyzer 37 | { 38 | private readonly Key key = new Key("ComplexityAnalyzerState"); 39 | 40 | #if RIDER 41 | private readonly ComplexityCodeInsightsProvider _provider; 42 | private readonly IconHost _iconHost; 43 | 44 | public ComplexityAnalysisElementProblemAnalyzer(ComplexityCodeInsightsProvider provider, IconHost iconHost) 45 | { 46 | _provider = provider; 47 | _iconHost = iconHost; 48 | } 49 | #endif 50 | 51 | protected override void Run(ITreeNode element, ElementProblemAnalyzerData data, IHighlightingConsumer consumer) 52 | { 53 | // We get a fresh data for each file, so we can cache some state to make 54 | // things a bit more efficient 55 | var state = data.GetOrCreateDataUnderLock(key, () => new State 56 | { 57 | ControlFlowBuilder = LanguageManager.Instance.TryGetService(element.Language), 58 | Threshold = GetThreshold(data, element.Language) 59 | }); 60 | 61 | if (state.ControlFlowBuilder == null || !state.ControlFlowBuilder.CanBuildFrom(element)) 62 | return; 63 | 64 | // We can build control flow information for a JS file section (e.g. inline event handlers, or 65 | // elements in html) and it would be nice to flag these up as being too complex, but there's nowhere 66 | // to put the warning - it highlights the whole section. Maybe that's ok for when we're over the threshold, 67 | // but it's definitely not ok for the info tooltip. 68 | if (element is IJavaScriptFileSection) 69 | return; 70 | 71 | var graph = data.GetOrBuildControlFlowGraph(element); 72 | if (graph == null) 73 | return; 74 | 75 | var complexity = CalculateCyclomaticComplexity(graph); 76 | var documentRange = GetDocumentRange(element); 77 | 78 | if (complexity > state.Threshold) 79 | { 80 | consumer.AddHighlighting( 81 | new ComplexityWarningHighlight( 82 | GetMessage(element, complexity, state.Threshold), 83 | documentRange)); 84 | } 85 | #if !RIDER 86 | else 87 | consumer.AddHighlighting(new ComplexityInfoHighlight(GetMessage(element, complexity, state.Threshold), documentRange)); 88 | #else 89 | if (element is ITypeMemberDeclaration memberDeclaration && memberDeclaration.DeclaredElement != null) 90 | { 91 | var percentage = GetPercentage(complexity, state.Threshold); 92 | consumer.AddHighlighting(new ComplexityCodeInsightsHighlight( 93 | memberDeclaration, 94 | complexity, 95 | percentage, 96 | _provider, 97 | _iconHost)); 98 | } 99 | #endif 100 | } 101 | 102 | private static int GetThreshold(ElementProblemAnalyzerData data, PsiLanguageType language) 103 | { 104 | var threshold = data.SettingsStore.GetIndexedValue((CyclomaticComplexityAnalysisSettings s) => s.Thresholds, language.Name); 105 | return threshold < 1 ? CyclomaticComplexityAnalysisSettings.DefaultThreshold : threshold; 106 | } 107 | 108 | private static int CalculateCyclomaticComplexity(IControlFlowGraph graph) 109 | { 110 | var edges = GetEdges(graph); 111 | var nodeCount = GetNodeCount(edges); 112 | 113 | // Standard CC formula (edges - nodes + 2) 114 | return edges.Count - nodeCount + 2; 115 | } 116 | 117 | // ReSharper's C# graph treats boolean values as conditions, meaning we 118 | // get two (duplicate) edges from one node to the next. This comparer 119 | // will remove the duplicates. I.e. any edges that have the same source 120 | // and target are considered equal 121 | private class EdgeEqualityComparer : EqualityComparer 122 | { 123 | public override bool Equals(IControlFlowEdge x, IControlFlowEdge y) 124 | { 125 | if (x == y) 126 | return true; 127 | if (x == null || y == null) 128 | return false; 129 | return x.Source == y.Source && x.Target == y.Target; 130 | } 131 | 132 | public override int GetHashCode(IControlFlowEdge obj) 133 | { 134 | var hashCode = 0; 135 | if (obj != null) 136 | { 137 | if (obj.Source != null) 138 | hashCode ^= obj.Source.GetHashCode(); 139 | 140 | if (obj.Target != null) 141 | hashCode ^= obj.Target.GetHashCode(); 142 | } 143 | 144 | return hashCode; 145 | } 146 | } 147 | 148 | private static HashSet GetEdges(IControlFlowGraph graph) 149 | { 150 | var edges = new HashSet(new EdgeEqualityComparer()); 151 | foreach (var element in graph.AllElements) 152 | { 153 | foreach (var edge in element.Exits) 154 | edges.Add(edge); 155 | foreach (var edge in element.Entries) 156 | edges.Add(edge); 157 | } 158 | return FudgeGraph(graph, edges); 159 | } 160 | 161 | private static HashSet FudgeGraph(IControlFlowGraph graph, HashSet edges) 162 | { 163 | // The C# graph treats the unary negation operator `!` as a conditional. It's not, 164 | // but having it so means that the CC can be way higher than it should be. 165 | var dodgyEdges = new HashSet(new EdgeEqualityComparer()); 166 | 167 | // Look at all of the non-leaf elements (the graph is a tree as well as a graph. 168 | // The tree represents the structure of the code, with the control flow graph 169 | // running through it) 170 | foreach (var element in graph.AllElements.Where(e => e.Children.Count != 0)) 171 | { 172 | var unaryOperatorExpression = element.SourceElement as IUnaryOperatorExpression; 173 | if (unaryOperatorExpression != null && unaryOperatorExpression.UnaryOperatorType == UnaryOperatorType.EXCL) 174 | { 175 | // The unary operator shouldn't have 2 exits. It's not a conditional. If it does, 176 | // remove one of the edges, and it's path, from the collected edges. 177 | if (element.Exits.Count == 2) 178 | { 179 | var edge = element.Exits.FirstOrDefault(); 180 | do 181 | { 182 | dodgyEdges.Add(edge); 183 | 184 | // Get the source of the exit edge. This is the node in the control flow graph, 185 | // not the element in the program structure tree. 186 | var source = edge.Source; 187 | edge = null; 188 | 189 | // Walk back to the control flow graph node that represents the exit of the 190 | // dodgy condition, so keep going until we have an element with 2 exits. 191 | if (source.Entries.Count == 1 && source.Exits.Count == 1) 192 | edge = source.Entries.FirstOrDefault(); 193 | } while (edge != null); 194 | } 195 | } 196 | } 197 | 198 | edges.ExceptWith(dodgyEdges); 199 | return edges; 200 | } 201 | 202 | private static int GetNodeCount(IEnumerable edges) 203 | { 204 | var hasNullDestination = false; 205 | 206 | var nodes = new HashSet(); 207 | foreach (var edge in edges) 208 | { 209 | nodes.Add(edge.Source); 210 | 211 | if (edge.Target != null) 212 | nodes.Add(edge.Target); 213 | else 214 | hasNullDestination = true; 215 | } 216 | return nodes.Count + (hasNullDestination ? 1 : 0); 217 | } 218 | 219 | private static string GetMessage(ITreeNode element, int complexity, int threshold) 220 | { 221 | var type = "Element"; 222 | IDeclaration declaration; 223 | GetBestTreeNode(element, out declaration); 224 | if (declaration?.DeclaredElement != null) 225 | { 226 | var declaredElement = declaration.DeclaredElement; 227 | var declarationType = DeclaredElementPresenter.Format(declaration.Language, 228 | DeclaredElementPresenter.KIND_PRESENTER, declaredElement); 229 | var declaredElementName = DeclaredElementPresenter.Format(declaration.Language, 230 | DeclaredElementPresenter.NAME_PRESENTER, declaredElement); 231 | 232 | type = $"{declarationType.Capitalize()} '{declaredElementName}'"; 233 | } 234 | 235 | return $"{type} has cyclomatic complexity of {complexity} ({GetPercentage(complexity, threshold)}% of threshold)"; 236 | } 237 | 238 | private static int GetPercentage(int complexity, int threshold) 239 | { 240 | return (int) (complexity*100.0/threshold); 241 | } 242 | 243 | private DocumentRange GetDocumentRange(ITreeNode element) 244 | { 245 | IDeclaration declaration; 246 | var node = GetBestTreeNode(element, out declaration); 247 | return declaration?.DeclaredElement != null ? declaration.GetNameDocumentRange() : node.GetDocumentRange(); 248 | } 249 | 250 | private static ITreeNode GetBestTreeNode(ITreeNode element, out IDeclaration declaration) 251 | { 252 | declaration = element as IDeclaration; 253 | if (declaration?.DeclaredElement != null) 254 | return element; 255 | 256 | // Don't have a declared element to highlight. Going to have to guess. Try the 257 | // first meaningful child 258 | return element.GetNextMeaningfulChild(null); 259 | } 260 | 261 | private class State 262 | { 263 | public IControlFlowBuilder ControlFlowBuilder; 264 | public int Threshold; 265 | } 266 | } 267 | } --------------------------------------------------------------------------------