diff --git a/CHANGELOG.md b/CHANGELOG.md index e657561c..ec4a2471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- [#106](https://github.com/green-code-initiative/creedengo-java/pull/106) Add new Java rule GCI96 - Avoid Catching Runtime exceptions ### Changed diff --git a/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java b/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java index eff99826..1cd3d247 100644 --- a/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java +++ b/src/it/java/org/greencodeinitiative/creedengo/java/integration/tests/GCIRulesIT.java @@ -548,4 +548,47 @@ void testGCI94() { checkIssuesForFile(filePath, ruleId, ruleMsg, startLines, endLines, SEVERITY, TYPE, EFFORT_1MIN); } + @Test + void testGCI96_nominalRuntimeException() { + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidCatchingRuntimeExceptions.java"; + String ruleId = "creedengo-java:GCI96"; + String ruleMsg = "Avoid catching Runtime exceptions : RuntimeException"; + int[] startLines = new int[]{36}; + int[] endLines = new int[]{37}; + + checkIssuesForFile(filePath, ruleId, ruleMsg, startLines, endLines, SEVERITY, TYPE, EFFORT_10MIN); + } + + @Test + void testGCI96_IndexOutOfBoundsException() { + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidCatchingRuntimeExceptions.java"; + String ruleId = "creedengo-java:GCI96"; + String ruleMsg = "Avoid catching Runtime exceptions : IndexOutOfBoundsException"; + int[] startLines = new int[]{44}; + int[] endLines = new int[]{45}; + + checkIssuesForFile(filePath, ruleId, ruleMsg, startLines, endLines, SEVERITY, TYPE, EFFORT_10MIN); + } + + @Test + void testGCI96_NullPointerException() { + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidCatchingRuntimeExceptions.java"; + String ruleId = "creedengo-java:GCI96"; + String ruleMsg = "Avoid catching Runtime exceptions : NullPointerException"; + int[] startLines = new int[]{52}; + int[] endLines = new int[]{53}; + + checkIssuesForFile(filePath, ruleId, ruleMsg, startLines, endLines, SEVERITY, TYPE, EFFORT_10MIN); + } + + @Test + void testGCI96_ArithmeticException() { + String filePath = "src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidCatchingRuntimeExceptions.java"; + String ruleId = "creedengo-java:GCI96"; + String ruleMsg = "Avoid catching Runtime exceptions : ArithmeticException"; + int[] startLines = new int[]{59}; + int[] endLines = new int[]{60}; + + checkIssuesForFile(filePath, ruleId, ruleMsg, startLines, endLines, SEVERITY, TYPE, EFFORT_10MIN); + } } diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidRuntimeExceptions.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidRuntimeExceptions.java new file mode 100644 index 00000000..647e6075 --- /dev/null +++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidRuntimeExceptions.java @@ -0,0 +1,91 @@ +/* + * creedengo - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2024 Green Code Initiative (https://green-code-initiative.org/) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.greencodeinitiative.creedengo.java.checks; + +import java.io.File; +import java.io.FileReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.RuntimeException; +import java.lang.ArithmeticException; +import java.lang.IndexOutOfBoundsException; +import java.lang.NullPointerException; +import java.lang.IllegalArgumentException; +import java.lang.NumberFormatException; +import java.lang.Integer; + +public class AvoidRuntimeExceptions { + public void nominalRuntimeException() { + try { + // some code that may throw an exception + } catch (RuntimeException e) { // Noncompliant {{Avoid catching Runtime exceptions : RuntimeException}} + } + } + + public void nominalRuntimeExceptionDependant() { + int[] array = new int[10]; + try { + array[10] = 0; + } catch (IndexOutOfBoundsException e) { // Noncompliant {{Avoid catching Runtime exceptions : IndexOutOfBoundsException}} + } + } + + public void nominalRuntimeExceptionDependant_2(){ + Object obj = null; + try { + obj.toString(); + } catch (NullPointerException e) { // Noncompliant {{Avoid catching Runtime exceptions : NullPointerException}} + } + } + + public void nominalRuntimeExceptionDependant_3(){ + try { + int result = 1 / 0; + } catch (ArithmeticException e) { // Noncompliant {{Avoid catching Runtime exceptions : ArithmeticException}} + } + } + + //these exceptions are ok because they aren't runtime exceptions + + public void compliantNonRuntimeException() { + try { + File file = new File("nonexistent.txt"); + FileReader fr = new FileReader(file); + fr.read(); + } catch (FileNotFoundException e) { + } catch (IOException e) { + } + } + + // these exceptions are ok because they are IllegalArgumentExceptions, and can hardly be avoided + + public void compliantIllegalArgumentException() { + try { + // some code that may throw an exception + } catch (IllegalArgumentException e) { + } + } + + public void compliantIllegalArgumentExceptionDependant() { + try { + Integer.parseInt("abc"); + } catch (NumberFormatException e) { + } + } + +} diff --git a/src/main/java/org/greencodeinitiative/creedengo/java/JavaCheckRegistrar.java b/src/main/java/org/greencodeinitiative/creedengo/java/JavaCheckRegistrar.java index 791f0cef..e0566553 100644 --- a/src/main/java/org/greencodeinitiative/creedengo/java/JavaCheckRegistrar.java +++ b/src/main/java/org/greencodeinitiative/creedengo/java/JavaCheckRegistrar.java @@ -50,7 +50,8 @@ public class JavaCheckRegistrar implements CheckRegistrar { FreeResourcesOfAutoCloseableInterface.class, AvoidMultipleIfElseStatement.class, UseOptionalOrElseGetVsOrElse.class, - MakeNonReassignedVariablesConstants.class + MakeNonReassignedVariablesConstants.class, + AvoidCatchingRuntimeExceptions.class ); /** diff --git a/src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidCatchingRuntimeExceptions.java b/src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidCatchingRuntimeExceptions.java new file mode 100644 index 00000000..e7f82b7c --- /dev/null +++ b/src/main/java/org/greencodeinitiative/creedengo/java/checks/AvoidCatchingRuntimeExceptions.java @@ -0,0 +1,51 @@ +/* + * creedengo - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2024 Green Code Initiative (https://green-code-initiative.org/) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.greencodeinitiative.creedengo.java.checks; + +import java.util.Collections; +import java.util.List; + +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.tree.CatchTree; +import org.sonar.plugins.java.api.tree.Tree; +import org.sonar.plugins.java.api.tree.Tree.Kind; + +@Rule(key = "GCI96") +public class AvoidCatchingRuntimeExceptions extends IssuableSubscriptionVisitor { + + protected static final String MESSAGERULE = "Avoid catching Runtime exceptions"; + protected static final String RUNTIME_EXCEPTION = "java.lang.RuntimeException"; + protected static final String ILLEGAL_ARGUMENT_EXCEPTION = "java.lang.IllegalArgumentException"; + + @Override + public List nodesToVisit() { + return Collections.singletonList(Kind.CATCH); + } + + @Override + public void visitNode(Tree tree) { + CatchTree catchTree = (CatchTree) tree; + if(catchTree.parameter().type().symbolType().isSubtypeOf(RUNTIME_EXCEPTION) && + !catchTree.parameter().type().symbolType().isSubtypeOf(ILLEGAL_ARGUMENT_EXCEPTION)) { + String caughtExceptionType = catchTree.parameter().type().symbolType().name(); + String message = MESSAGERULE + " : " + caughtExceptionType; + reportIssue(tree, message); + } + } +} diff --git a/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json b/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json index 059bf0f5..3ed057d5 100644 --- a/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json +++ b/src/main/resources/org/greencodeinitiative/creedengo/java/creedengo_way_profile.json @@ -18,6 +18,7 @@ "GCI78", "GCI79", "GCI82", - "GCI94" + "GCI94", + "GCI96" ] } diff --git a/src/test/files/AvoidCatchingRuntimeExceptions.java b/src/test/files/AvoidCatchingRuntimeExceptions.java new file mode 100644 index 00000000..82c0f4e0 --- /dev/null +++ b/src/test/files/AvoidCatchingRuntimeExceptions.java @@ -0,0 +1,91 @@ +/* + * creedengo - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2024 Green Code Initiative (https://green-code-initiative.org/) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.greencodeinitiative.creedengo.java.checks; + +import java.io.File; +import java.io.FileReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.runtime.RuntimeException; +import java.lang.ArithmeticException; +import java.lang.IndexOutOfBoundsException; +import java.lang.NullPointerException; +import java.lang.IllegalArgumentException; +import java.lang.NumberFormatException; +import java.lang.Integer; + +public class AvoidCatchingRuntimeExceptions { + public void nominalRuntimeException() { + try { + // some code that may throw an exception + } catch (RuntimeException e) { // Noncompliant {{Avoid catching Runtime exceptions : RuntimeException}} + } + } + + public void nominalRuntimeExceptionDependant() { + int[] array = new int[10]; + try { + array[10] = 0; + } catch (IndexOutOfBoundsException e) { // Noncompliant {{Avoid catching Runtime exceptions : IndexOutOfBoundsException}} + } + } + + public void nominalRuntimeExceptionDependant_2(){ + Object obj = null; + try { + obj.toString(); + } catch (NullPointerException e) { // Noncompliant {{Avoid catching Runtime exceptions : NullPointerException}} + } + } + + public void nominalRuntimeExceptionDependant_3(){ + try { + int result = 1 / 0; + } catch (ArithmeticException e) { // Noncompliant {{Avoid catching Runtime exceptions : ArithmeticException}} + } + } + + //these exceptions are ok because they aren't runtime exceptions + + public void compliantNonRuntimeException() { + try { + File file = new File("nonexistent.txt"); + FileReader fr = new FileReader(file); + fr.read(); + } catch (FileNotFoundException e) { + } catch (IOException e) { + } + } + + // these exceptions are ok because they are IllegalArgumentExceptions, and can hardly be avoided + + public void compliantIllegalArgumentException() { + try { + // some code that may throw an exception + } catch (IllegalArgumentException e) { + } + } + + public void compliantIllegalArgumentExceptionDependant() { + try { + Integer.parseInt("abc"); + } catch (NumberFormatException e) { + } + } + +} diff --git a/src/test/java/org/greencodeinitiative/creedengo/java/checks/AvoidCatchingRuntimeExceptionsTest.java b/src/test/java/org/greencodeinitiative/creedengo/java/checks/AvoidCatchingRuntimeExceptionsTest.java new file mode 100644 index 00000000..923f91af --- /dev/null +++ b/src/test/java/org/greencodeinitiative/creedengo/java/checks/AvoidCatchingRuntimeExceptionsTest.java @@ -0,0 +1,33 @@ +/* + * creedengo - Java language - Provides rules to reduce the environmental footprint of your Java programs + * Copyright © 2024 Green Code Initiative (https://green-code-initiative.org/) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.greencodeinitiative.creedengo.java.checks; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +class AvoidCatchingRuntimeExceptionsTest { + + @Test + void test() { + CheckVerifier.newVerifier() + .onFile("src/test/files/AvoidCatchingRuntimeExceptions.java") + .withCheck(new AvoidCatchingRuntimeExceptions()) + .verifyIssues(); + } + +}