├── 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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/.idea.CyclomaticComplexity/.idea/runConfigurations/Rider__Windows_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/.idea.ReSharperPlugin.CyclomaticComplexity/.idea/runConfigurations/rdgen__Unix_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/.idea.CyclomaticComplexity/.idea/runConfigurations/rdgen__Windows_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/.idea.ReSharperPlugin.CyclomaticComplexity/.idea/runConfigurations/rdgen__Windows_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/.idea.ReSharperPlugin.CyclomaticComplexity/.idea/runConfigurations/Rider__Frontend__Windows_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/Rider.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
16 |
17 |
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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/.idea.ReSharperPlugin.CyclomaticComplexity/.idea/runConfigurations/Rider__Windows_.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
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 | [](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
2 |
3 | [](https://plugins.jetbrains.com/plugin/10395-cyclomatic-complexity)
4 | [](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 | 
17 |
18 | When the complexity reaches a configurable threshold, the tooltip is shown as a warning:
19 |
20 | 
21 |
22 | And the complexity threshold can be configured in the options, per language:
23 |
24 | 
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 | }
--------------------------------------------------------------------------------