isPvP
has a cyclomatic complexity of 25 with "High" risk104 return item.getType().name();
105 }
106
107 public static final boolean isPvP(final EntityDamageByEntityEvent event) {108 final Entity attacker = event.getDamager();
109 final Entity defender = event.getEntity();
110
checkProtection
has a cyclomatic complexity of 15 with "Medium" risk 60 addOnlinePlayers();
61 }
62
63 public final ProtectionResult checkProtection(@NotNull final Player damager, @NotNull final Player defender) { 64 final CombatPlayer attacker = get(damager);
65 final CombatPlayer attacked = get(defender);
66
onPlayerInteract
has a cyclomatic complexity of 17 with "High" risk177
178 @SuppressWarnings("null") // p.getLocation() is not null
179 @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) // cancel on low since some plugins check cancels on normal instead of monitor
180 public final void onPlayerInteract(final PlayerInteractEvent e) {181 final Player player = e.getPlayer();
182 if (CombatUtils.isWorldExcluded(player.getWorld().getName()) || e.getAction() != Action.RIGHT_CLICK_BLOCK)
183 return;
disableActions
has a cyclomatic complexity of 16 with "High" risk155 }
156
157 @SuppressWarnings("null") // PotionEffectType.INVISIBILITY is not null
158 private void disableActions(final Player attacker, final Player defender, final CombatPlayer pvpAttacker, final CombatPlayer pvpDefender) {159 final boolean hasExemptPerm = pvpAttacker.hasPerm(Permissions.EXEMPT_DISABLE_ACTIONS);
160 if (Conf.DISABLE_FLY.asBool()) {
161 if (CombatUtils.canFly(attacker) && !hasExemptPerm) {
onDamageActions
has a cyclomatic complexity of 19 with "High" risk120 }
121
122 @SuppressWarnings("null") // defender.getLocation() never null
123 public void onDamageActions(final Player attacker, final Player defender) {124 final CombatPlayer pvpAttacker = ph.get(attacker);
125 final CombatPlayer pvpDefender = ph.get(defender);
126
initMetrics
has a cyclomatic complexity of 41 with "Very High" risk 19 initMetrics(plugin);
20 }
21
22 private void initMetrics(final PvPManager plugin) { 23
24 final Metrics metrics = new Metrics(plugin, 5653, Conf.METRICS_OPT_OUT.asBool());
25
onPlaceholderRequest
has a cyclomatic complexity of 17 with "High" risk 39 }
40
41 @Override
42 public String onPlaceholderRequest(final Player player, final String identifier) { 43 if (player == null)
44 return "";
45
onCommand
has a cyclomatic complexity of 15 with "Medium" risk 30 }
31
32 @Override
33 public final boolean onCommand(final CommandSender sender, final Command cmd, final String label, final String[] args) { 34 if (!Permissions.COMMAND_PVP_TOGGLE.hasPerm(sender)) {
35 sender.sendMessage(Lang.ERROR_PERMISSION.msg());
36 return true;
debug
has a cyclomatic complexity of 20 with "High" risk171 });
172 }
173
174 private void debug(final CommandSender sender, final String[] args) {175 CombatPlayer p = null;
176 if (args.length == 2 && sender instanceof final Player player) {
177 p = plugin.getPlayerManager().get(player);
onCommand
has a cyclomatic complexity of 18 with "High" risk 61 }
62
63 @Override
64 public final boolean onCommand(final CommandSender sender, final Command cmd, final String label, final String[] args) { 65 if (args.length == 0 && Permissions.COMMAND_MENU.hasPerm(sender)) {
66 helpMenu(sender);
67 return true;
A function with high cyclomatic complexity can be hard to understand and maintain. Cyclomatic complexity is a software metric that measures the number of independent paths through a function. A higher cyclomatic complexity indicates that the function has more decision points and is more complex.
Functions with high cyclomatic complexity are more likely to have bugs and be harder to test. They may lead to reduced code maintainability and increased development time.
To reduce the cyclomatic complexity of a function, you can:
- Break the function into smaller, more manageable functions.
- Refactor complex logic into separate functions or classes.
- Avoid multiple return paths and deeply nested control expressions.
Bad practice
The method below (from the source code of the Maven build system, non-branch lines have been abbreviated) has a complexity of 25, and should be refactored if possible.
public VersionResult resolveVersion(RepositorySystemSession session, VersionRequest request) // 1
throws VersionResolutionException {
// ...
if (cache != null && !ConfigUtils.getBoolean(session, false, "aether.versionResolver.noCache")) { // +2
// ...
if (obj instanceof Record) { // +1
}
}
Metadata metadata = null;
// This section could be refactored, as all operations here are independent of external control flow.
if (RELEASE.equals(version)) { // +1
metadata = new DefaultMetadata(
artifact.getGroupId(), artifact.getArtifactId(), MAVENMETADATAXML, Metadata.Nature.RELEASE);
} else if (LATEST.equals(version)) { // +1
metadata = new DefaultMetadata(
artifact.getGroupId(),
artifact.getArtifactId(),
MAVENMETADATAXML,
Metadata.Nature.RELEASEORSNAPSHOT);
} else if (version.endsWith(SNAPSHOT)) { // +1
WorkspaceReader workspace = session.getWorkspaceReader();
if (workspace != null && workspace.findVersions(artifact).contains(version)) { // +2
metadata = null;
result.setRepository(workspace.getRepository());
} else {
metadata = new DefaultMetadata(
artifact.getGroupId(),
artifact.getArtifactId(),
version,
MAVENMETADATAXML,
Metadata.Nature.SNAPSHOT);
}
} else {
metadata = null;
}
if (metadata == null) { // +1
result.setVersion(version);
} else {
// ...
for (RemoteRepository repository : request.getRepositories()) { // +1
// ...
}
// ...
for (MetadataResult metadataResult : metadataResults) { // +1
// ...
if (repository == null) { // +1
// ...
}
Versioning v = readVersions(session, trace, metadataResult.getMetadata(), repository, result);
merge(artifact, infos, v, repository);
}
// This section could also be extracted for the same reasons.
if (RELEASE.equals(version)) { // +1
resolve(result, infos, RELEASE);
} else if (LATEST.equals(version)) { // +1
if (!resolve(result, infos, LATEST)) { // +1
resolve(result, infos, RELEASE);
}
if (result.getVersion() != null && result.getVersion().endsWith(SNAPSHOT)) { // +2
VersionRequest subRequest = new VersionRequest();
subRequest.setArtifact(artifact.setVersion(result.getVersion()));
if (result.getRepository() instanceof RemoteRepository) { // +1
RemoteRepository r = (RemoteRepository) result.getRepository();
subRequest.setRepositories(Collections.singletonList(r));
} else {
subRequest.setRepositories(request.getRepositories());
}
VersionResult subResult = resolveVersion(session, subRequest);
result.setVersion(subResult.getVersion());
result.setRepository(subResult.getRepository());
for (Exception exception : subResult.getExceptions()) { // +1
result.addException(exception);
}
}
} else {
String key = SNAPSHOT + getKey(artifact.getClassifier(), artifact.getExtension());
merge(infos, SNAPSHOT, key);
if (!resolve(result, infos, key)) { // +1
result.setVersion(version);
}
}
if (StringUtils.isEmpty(result.getVersion())) { // +1
throw new VersionResolutionException(result);
}
}
if (cacheKey != null && metadata != null && isSafelyCacheable(session, artifact)) { // +3
cache.put(session, cacheKey, new Record(result.getVersion(), result.getRepository()));
}
return result;
}
Recommended
It is best to refactor the method into multiple separate methods, so that the complexity of individual methods is reduced.
Here, after extracting the parts of the code highlighted above, the complexity is reduced to 12
, and shifted into two other methods instead.
public VersionResult resolveVersion(RepositorySystemSession session, VersionRequest request) // 1
throws VersionResolutionException {
// ...
if (cache != null && !ConfigUtils.getBoolean(session, false, "aether.versionResolver.noCache")) { // +2
// ...
if (obj instanceof Record) { // +1
// ...
}
}
Metadata metadata = getMetadataForVersion(session, version, artifact, result);
if (metadata == null) { // +1
// ...
} else {
// ...
for (RemoteRepository repository : request.getRepositories()) { // +1
// ...
}
// ...
for (MetadataResult metadataResult : metadataResults) { // +1
// ...
if (repository == null) { // +1
// ...
}
// ...
}
resolveBasedOnVersion(session, request, version, result, infos, artifact);
if (StringUtils.isEmpty(result.getVersion())) { // +1
throw new VersionResolutionException(result);
}
}
if (cacheKey != null && metadata != null && isSafelyCacheable(session, artifact)) { // +3
cache.put(session, cacheKey, new Record(result.getVersion(), result.getRepository()));
}
return result;
}
private void resolveBasedOnVersion(RepositorySystemSession session, VersionRequest request, String version, VersionResult result, Map<String, VersionInfo> infos, Artifact artifact) throws VersionResolutionException {
if (RELEASE.equals(version)) { // +1
resolve(result, infos, RELEASE);
} else if (LATEST.equals(version)) { // +1
if (!resolve(result, infos, LATEST)) { // +1
resolve(result, infos, RELEASE);
}
if (result.getVersion() != null && result.getVersion().endsWith(SNAPSHOT)) { // +2
// ...
if (result.getRepository() instanceof RemoteRepository) { // +1
// ...
} else {
// ...
}
// ...
for (Exception exception : subResult.getExceptions()) { // +1
result.addException(exception);
}
}
} else {
// ...
if (!resolve(result, infos, key)) { // +1
result.setVersion(version);
}
}
}
@Nullable
private static Metadata getMetadataForVersion(RepositorySystemSession session, String version, Artifact artifact, VersionResult result) {
if (RELEASE.equals(version)) { // +1
// ...
} else if (LATEST.equals(version)) { // +1
// ...
} else if (version.endsWith(SNAPSHOT)) { // +1
WorkspaceReader workspace = session.getWorkspaceReader();
if (workspace != null && workspace.findVersions(artifact).contains(version)) { // +2
// ...
} else {
// ...
}
} else {
metadata = null;
}
return metadata;
}
Issue configuration
Cyclomatic complexity threshold can be configured using the
cyclomatic_complexity_threshold
meta field in your repository's
.deepsource.toml
config file.
Configuring this is optional. If you don't provide a value, the Analyzer will
raise issues for functions with complexity higher than the default threshold,
which is "medium" (which raises issues for complexity > 15
) for the Java Analyzer.
Here's a mapping of risk category to cyclomatic complexity score to help you configure this better:
Risk category | Cyclomatic complexity range | Recommended action |
---|---|---|
low | 1-5 | No action needed. |
medium | 6-15 | Review and monitor. |
high | 16-25 | Review and refactor. It is recommended to add explanatory comments if the function absolutely cannot be changed. |
very-high | 26-50 | Refactor to reduce the complexity. |
critical | >50 | The function must be refactored. Such high complexity can harm testability and readability. |