diff --git a/docs/java/rules/S8795.md b/docs/java/rules/S8795.md new file mode 100644 index 00000000000..9ae89e3d3c6 --- /dev/null +++ b/docs/java/rules/S8795.md @@ -0,0 +1,420 @@ +# Technical Implementation Specification: Code After Unconditional Control Flow Statements (S8795) + +## Overview + +This rule detects statements that appear after unconditional jump statements (`return`, `throw`, `break`, `continue`) and can therefore never be executed. It is a port of rule S1763 to Java, already implemented in JavaScript, Python, Go, Kotlin, Ruby, Scala, Swift, Apex, C#, and PHP. + +**Issue message:** `Remove this unreachable code.` +**Secondary location message (on the jump statement):** `"Control flow is transferred here."` + +--- + +## 1. Implementation Approach + +### Base Class Selection + +Use **`IssuableSubscriptionVisitor`** — this rule works purely at the AST level, traversing statement lists within blocks to find statements that follow an unconditional jump. + +### AST Node Types to Visit + +Subscribe to the following node types, which all contain statement blocks: + +```java +@Override +public List nodesToVisit() { + return Arrays.asList( + Tree.Kind.METHOD, + Tree.Kind.CONSTRUCTOR, + Tree.Kind.STATIC_INITIALIZER, + Tree.Kind.INITIALIZER + ); +} +``` + +### Class Structure + +```java +@Rule(key = "S8795") +public class UnreachableCodeCheck extends IssuableSubscriptionVisitor { + + @Override + public List nodesToVisit() { + return Arrays.asList( + Tree.Kind.METHOD, + Tree.Kind.CONSTRUCTOR, + Tree.Kind.STATIC_INITIALIZER, + Tree.Kind.INITIALIZER + ); + } + + @Override + public void visitNode(Tree tree) { + BlockTree block = extractBlock(tree); + if (block != null) { + checkStatements(block.body()); + } + } +} +``` + +--- + +## 2. Detection Logic + +### Core Algorithm + +Iterate through a list of statements in order. Once an unconditional jump statement is encountered, the immediately following non-empty statement is unreachable and should be reported. Only the **first** unreachable statement is reported per block. + +```java +private void checkStatements(List statements) { + StatementTree jumpStatement = null; + for (StatementTree statement : statements) { + if (jumpStatement != null) { + // 'statement' is the first unreachable statement + reportIssue( + statement, + "Remove this unreachable code.", + List.of(new JavaFileScannerContext.Location("Control flow is transferred here.", jumpStatement)), + null + ); + return; // Report only the first unreachable statement per block + } + if (isUnconditionalJump(statement)) { + jumpStatement = statement; + } + // Recurse into nested blocks + visitNestedBlocks(statement); + } +} +``` + +### Unconditional Jump Detection + +A statement is an unconditional jump if it is one of: + +```java +private static boolean isUnconditionalJump(StatementTree statement) { + return statement.is( + Tree.Kind.RETURN_STATEMENT, + Tree.Kind.THROW_STATEMENT, + Tree.Kind.BREAK_STATEMENT, + Tree.Kind.CONTINUE_STATEMENT + ); +} +``` + +All four are unconditional in the sense that, once reached, they always transfer control away from the subsequent statement in the same block. Labeled `break` and `continue` are also covered since they are still of kind `BREAK_STATEMENT` / `CONTINUE_STATEMENT`. + +--- + +## 3. Nested Block Traversal + +The check must recursively descend into nested blocks to catch issues such as: + +```java +// Code after continue inside an if-body +for (String item : items) { + if (item.isEmpty()) { + continue; + System.out.println(item); // Noncompliant + } +} +``` + +Implement a `visitNestedBlocks` method that dispatches on statement kind: + +```java +private void visitNestedBlocks(StatementTree statement) { + switch (statement.kind()) { + case IF_STATEMENT: { + IfStatementTree ifStmt = (IfStatementTree) statement; + checkBlock(ifStmt.thenStatement()); + if (ifStmt.elseStatement() != null) { + checkBlock(ifStmt.elseStatement()); + } + break; + } + case FOR_STATEMENT: + checkBlock(((ForStatementTree) statement).statement()); + break; + case FOR_EACH_STATEMENT: + checkBlock(((ForEachStatement) statement).statement()); + break; + case WHILE_STATEMENT: + checkBlock(((WhileStatementTree) statement).statement()); + break; + case DO_STATEMENT: + checkBlock(((DoWhileStatementTree) statement).statement()); + break; + case SYNCHRONIZED_STATEMENT: + checkStatements(((SynchronizedStatementTree) statement).block().body()); + break; + case LABELED_STATEMENT: + checkBlock(((LabeledStatementTree) statement).statement()); + break; + case TRY_STATEMENT: + visitTryStatement((TryStatementTree) statement); + break; + case SWITCH_STATEMENT: + visitSwitchStatement((SwitchStatementTree) statement); + break; + default: + break; + } +} + +private void checkBlock(StatementTree statement) { + if (statement.is(Tree.Kind.BLOCK)) { + checkStatements(((BlockTree) statement).body()); + } else { + // Single-statement body (e.g., if (x) return;) + // A single statement cannot have unreachable code after it in the same block + } +} +``` + +--- + +## 4. Try-Finally Handling + +**Critical edge case:** Code in a `finally` block is always reachable, even if the `try` block ends with `return` or `throw`. Do **not** report issues inside `finally` blocks based on jumps in `try` blocks. Instead, analyse each section independently: + +```java +private void visitTryStatement(TryStatementTree tryStatement) { + // Analyse try block independently + checkStatements(tryStatement.block().body()); + + // Analyse each catch block independently + for (CatchTree catchTree : tryStatement.catches()) { + checkStatements(catchTree.block().body()); + } + + // Analyse finally block independently (always reachable) + if (tryStatement.finallyBlock() != null) { + checkStatements(tryStatement.finallyBlock().body()); + } +} +``` + +**Do NOT** flag statements after a `return` in a `try` block that appear in the `finally` block. + +--- + +## 5. Switch Statement Handling + +Each case group in a switch statement is analysed independently. A `break` or `return` in one case does not make statements in the next case unreachable (fall-through is intentional). Analyse the statement list inside each `CaseGroupTree` separately: + +```java +private void visitSwitchStatement(SwitchStatementTree switchStatement) { + for (CaseGroupTree caseGroup : switchStatement.cases()) { + checkStatements(caseGroup.body()); + } +} +``` + +Within a single case group, a `break`, `return`, `throw`, or `continue` makes subsequent statements in that same group unreachable: + +```java +switch (x) { + case 1: + return "one"; + System.out.println("unreachable"); // Noncompliant + case 2: + System.out.println("two"); // Compliant - separate case group +} +``` + +--- + +## 6. False Positive Avoidance + +### Only Analyse Direct Statement Sequences + +Report issues only when the unreachable statement is a **direct sibling** in the same statement list as the jump. Do not attempt interprocedural or cross-block analysis. + +### Do Not Report Inside Finally + +As described in §4, never use jumps in `try`/`catch` blocks to flag statements in `finally`. + +### Labeled Statements + +A labeled `break` or `continue` still constitutes an unconditional jump from the perspective of the enclosing block. The label does not create a scope boundary that changes reachability in the same statement list: + +```java +outer: +for (...) { + for (...) { + break outer; + doSomething(); // Noncompliant + } +} +``` + +--- + +## 7. Issue Reporting + +### Primary Location + +Report on the **first unreachable statement** following the jump. + +### Secondary Location + +Add a secondary location pointing to the **jump statement** itself with the message `"Control flow is transferred here."`. + +```java +reportIssue( + firstUnreachableStatement, + "Remove this unreachable code.", + List.of(new JavaFileScannerContext.Location( + "Control flow is transferred here.", + jumpStatement + )), + null +); +``` + +--- + +## 8. Code Examples + +### Example 1 — After `return` + +```java +// Noncompliant +public int calculateValue(int a) { + int i = 10; + return i + a; + i++; // Noncompliant +} + +// Compliant +public int calculateValue(int a) { + int i = 10; + i++; + return i + a; +} +``` + +### Example 2 — After `continue` inside `if` + +```java +// Noncompliant +public void processItems(List items) { + for (String item : items) { + if (item.isEmpty()) { + continue; + System.out.println("Processing: " + item); // Noncompliant + } + } +} + +// Compliant +public void processItems(List items) { + for (String item : items) { + if (item.isEmpty()) { + continue; + } + System.out.println("Processing: " + item); + } +} +``` + +### Example 3 — After `throw` + +```java +// Noncompliant +public void handleError() { + throw new IllegalStateException("Error occurred"); + cleanup(); // Noncompliant +} + +// Compliant +public void handleError() { + cleanup(); + throw new IllegalStateException("Error occurred"); +} +``` + +### Example 4 — Redundant `break` after `return` in switch + +```java +// Noncompliant +public String findFirst(List items, String target) { + for (String item : items) { + if (item.equals(target)) { + return item; + break; // Noncompliant: return already exits + } + } + return null; +} + +// Compliant +public String findFirst(List items, String target) { + for (String item : items) { + if (item.equals(target)) { + return item; + } + } + return null; +} +``` + +### Example 5 — `finally` block is NOT unreachable + +```java +// Compliant — finally always executes +public void process() { + try { + return; + } finally { + cleanup(); // Compliant: finally blocks always execute + } +} +``` + +### Example 6 — Multiple unreachable statements (only first reported) + +```java +// Only the first unreachable statement is reported +public void method() { + return; + int x = 1; // Noncompliant (only this one reported) + int y = 2; // No secondary issue raised here +} +``` + +--- + +## 9. Test File Conventions + +The test file should be placed at: +``` +java-checks-test-sources/default/src/main/java/checks/S8795CheckSample.java +``` + +Test cases should cover: +- `return` with and without expression +- `throw` statement +- `break` and `continue` in loops and switch +- Labeled `break`/`continue` +- Nested `if`/`for`/`while`/`do-while` blocks +- `try-catch-finally` (finally must NOT be flagged) +- Switch case groups (each case analysed independently) +- Multiple unreachable statements (only first flagged per block) +- Single-statement bodies (`if (x) return;` — no issue) + +--- + +## 10. Reference Implementations + +### Similar checks in sonar-java + +- **`RedundantJumpCheck` (S3626):** Detects redundant `return`/`continue` using CFG. Shows how to handle CFG blocks for jump detection; our rule uses simpler AST traversal since we detect *subsequent statements*, not redundant jumps. +- **`DeadStoreCheck` (S1854):** Uses CFG + LiveVariables for data-flow. Our rule is simpler — AST-only. +- **`UnreachableCatchCheck` (S4970):** Detects unreachable catch clauses using semantic type hierarchy. Our rule is syntactic. + +### Cross-language reference + +S1763 is already implemented in: JavaScript (`NoAllCodeShouldBeReachable`), Python, Go, Kotlin, Ruby, Scala, Swift, Apex, C#, PHP. The Java implementation follows the same logical approach adapted to the sonar-java AST API. diff --git a/its/sources b/its/sources deleted file mode 160000 index 7da42c6ae86..00000000000 --- a/its/sources +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7da42c6ae8610a6d6e90696c03a6f9cae0b16a82 diff --git a/java-checks-test-sources/default/src/main/java/checks/S8795CheckSample.java b/java-checks-test-sources/default/src/main/java/checks/S8795CheckSample.java new file mode 100644 index 00000000000..043dffd2d0c --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/S8795CheckSample.java @@ -0,0 +1,309 @@ +package checks; + +import java.util.List; + +public class S8795CheckSample { + + // =========================== + // return statement + // =========================== + + int afterReturnWithExpression(int a) { + int i = 10; + return i + a; + i++; // Noncompliant {{Remove this unreachable code.}} + } + + void afterReturnVoid() { + System.out.println("before"); + return; + System.out.println("after"); // Noncompliant {{Remove this unreachable code.}} + } + + void multipleUnreachableOnlyFirstFlagged() { + return; + int x = 1; // Noncompliant {{Remove this unreachable code.}} + int y = 2; // Compliant - only first unreachable is reported per block + } + + int compliantReturn(int a) { + int i = 10; + i++; + return i + a; // Compliant + } + + // =========================== + // throw statement + // =========================== + + void afterThrow() { + throw new IllegalStateException("Error occurred"); + cleanup(); // Noncompliant {{Remove this unreachable code.}} + } + + void compliantThrow() { + cleanup(); + throw new IllegalStateException("Error occurred"); // Compliant + } + + // =========================== + // break statement + // =========================== + + void afterBreakInLoop(List items) { + for (String item : items) { + break; + System.out.println(item); // Noncompliant {{Remove this unreachable code.}} + } + } + + void compliantBreakInLoop(List items) { + for (String item : items) { + if (item.isEmpty()) { + break; // Compliant + } + System.out.println(item); // Compliant + } + } + + void afterBreakInWhile() { + while (true) { + break; + System.out.println("unreachable"); // Noncompliant {{Remove this unreachable code.}} + } + } + + // =========================== + // continue statement + // =========================== + + void afterContinueInLoop(List items) { + for (String item : items) { + continue; + System.out.println(item); // Noncompliant {{Remove this unreachable code.}} + } + } + + void afterContinueInsideIf(List items) { + for (String item : items) { + if (item.isEmpty()) { + continue; + System.out.println("Processing: " + item); // Noncompliant {{Remove this unreachable code.}} + } + } + } + + void compliantContinueInsideIf(List items) { + for (String item : items) { + if (item.isEmpty()) { + continue; // Compliant + } + System.out.println("Processing: " + item); // Compliant + } + } + + void afterContinueInForEach(List items) { + for (String item : items) { + if (item.isEmpty()) { + continue; + System.out.println(item); // Noncompliant {{Remove this unreachable code.}} + } + } + } + + // =========================== + // nested if/else blocks + // =========================== + + void afterReturnInIfBlock(int x) { + if (x > 0) { + return; + System.out.println("positive"); // Noncompliant {{Remove this unreachable code.}} + } + System.out.println("done"); // Compliant + } + + void afterReturnInElseBlock(int x) { + if (x > 0) { + System.out.println("positive"); // Compliant + } else { + return; + System.out.println("non-positive"); // Noncompliant {{Remove this unreachable code.}} + } + } + + // =========================== + // switch statement + // =========================== + + String afterReturnInSwitchCase(int x) { + switch (x) { + case 1: + return "one"; + System.out.println("unreachable"); // Noncompliant {{Remove this unreachable code.}} + case 2: + return "two"; // Compliant - separate case group + default: + return "other"; + } + } + + void afterBreakInSwitchCase(int x) { + switch (x) { + case 1: + System.out.println("one"); + break; + System.out.println("unreachable"); // Noncompliant {{Remove this unreachable code.}} + case 2: + System.out.println("two"); // Compliant - separate case group + break; + } + } + + // =========================== + // try-catch-finally + // =========================== + + void afterReturnInTryBlock() { + try { + return; + System.out.println("unreachable in try"); // Noncompliant {{Remove this unreachable code.}} + } finally { + cleanup(); // Compliant - finally always executes + } + } + + void afterThrowInCatchBlock() { + try { + doSomething(); + } catch (Exception e) { + throw new RuntimeException(e); + cleanup(); // Noncompliant {{Remove this unreachable code.}} + } + } + + void finallyIsAlwaysReachable() { + try { + return; // Compliant + } finally { + cleanup(); // Compliant - finally blocks always execute + } + } + + void independentTryAndCatchAnalysis() { + try { + doSomething(); // Compliant + } catch (Exception e) { + System.out.println("caught"); // Compliant + } finally { + cleanup(); // Compliant + } + } + + // =========================== + // while / do-while loops + // =========================== + + void afterReturnInWhile() { + while (true) { + return; + System.out.println("unreachable"); // Noncompliant {{Remove this unreachable code.}} + } + } + + void afterBreakInDoWhile() { + do { + break; + System.out.println("unreachable"); // Noncompliant {{Remove this unreachable code.}} + } while (true); + } + + // =========================== + // labeled break / continue + // =========================== + + void afterLabeledBreak() { + outer: + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + break outer; + System.out.println("unreachable"); // Noncompliant {{Remove this unreachable code.}} + } + } + } + + void afterLabeledContinue() { + outer: + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + continue outer; + System.out.println("unreachable"); // Noncompliant {{Remove this unreachable code.}} + } + } + } + + // =========================== + // single-statement bodies (no issue possible) + // =========================== + + void singleStatementIfBody(boolean cond) { + if (cond) return; // Compliant - single statement, no sibling after it in same block + System.out.println("after if"); // Compliant - not in the if block + } + + void singleStatementForBody(List items) { + for (String item : items) + continue; // Compliant - single statement body + } + + // =========================== + // static initializer and instance initializer + // =========================== + + static { + if (Math.random() > 0.5) { + return; // Compliant (not a jump in the static block's top-level sequence) + } + System.out.println("static init"); // Compliant + } + + // =========================== + // constructor + // =========================== + + S8795CheckSample(boolean failFast) { + if (failFast) { + throw new IllegalArgumentException("fail fast"); + System.out.println("unreachable"); // Noncompliant {{Remove this unreachable code.}} + } + System.out.println("constructed"); // Compliant + } + + S8795CheckSample() { + // Compliant default constructor + } + + // =========================== + // redundant break after return (inside loop) + // =========================== + + String findFirst(List items, String target) { + for (String item : items) { + if (item.equals(target)) { + return item; + break; // Noncompliant {{Remove this unreachable code.}} + } + } + return null; + } + + // =========================== + // helpers + // =========================== + + private void cleanup() { + } + + private void doSomething() throws Exception { + } +} diff --git a/java-checks/src/main/java/org/sonar/java/checks/S8795Check.java b/java-checks/src/main/java/org/sonar/java/checks/S8795Check.java new file mode 100644 index 00000000000..ef1cf591cad --- /dev/null +++ b/java-checks/src/main/java/org/sonar/java/checks/S8795Check.java @@ -0,0 +1,161 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * 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 Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.checks; + +import java.util.Arrays; +import java.util.List; +import org.sonar.check.Rule; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.JavaFileScannerContext; +import org.sonar.plugins.java.api.tree.BlockTree; +import org.sonar.plugins.java.api.tree.CaseGroupTree; +import org.sonar.plugins.java.api.tree.CatchTree; +import org.sonar.plugins.java.api.tree.DoWhileStatementTree; +import org.sonar.plugins.java.api.tree.ForEachStatement; +import org.sonar.plugins.java.api.tree.ForStatementTree; +import org.sonar.plugins.java.api.tree.IfStatementTree; +import org.sonar.plugins.java.api.tree.LabeledStatementTree; +import org.sonar.plugins.java.api.tree.MethodTree; +import org.sonar.plugins.java.api.tree.StatementTree; +import org.sonar.plugins.java.api.tree.SwitchStatementTree; +import org.sonar.plugins.java.api.tree.SynchronizedStatementTree; +import org.sonar.plugins.java.api.tree.Tree; +import org.sonar.plugins.java.api.tree.TryStatementTree; +import org.sonar.plugins.java.api.tree.WhileStatementTree; + +@Rule(key = "S8795") +public class S8795Check extends IssuableSubscriptionVisitor { + + private static final String ISSUE_MESSAGE = "Remove this unreachable code."; + private static final String SECONDARY_MESSAGE = "Control flow is transferred here."; + + @Override + public List nodesToVisit() { + return Arrays.asList( + Tree.Kind.METHOD, + Tree.Kind.CONSTRUCTOR, + Tree.Kind.STATIC_INITIALIZER, + Tree.Kind.INITIALIZER); + } + + @Override + public void visitNode(Tree tree) { + BlockTree block = extractBlock(tree); + if (block != null) { + checkStatements(block.body()); + } + } + + private static BlockTree extractBlock(Tree tree) { + if (tree.is(Tree.Kind.METHOD, Tree.Kind.CONSTRUCTOR)) { + return ((MethodTree) tree).block(); + } else if (tree.is(Tree.Kind.STATIC_INITIALIZER, Tree.Kind.INITIALIZER)) { + return (BlockTree) tree; + } + return null; + } + + private void checkStatements(List statements) { + StatementTree jumpStatement = null; + for (StatementTree statement : statements) { + if (jumpStatement != null) { + reportIssue( + statement, + ISSUE_MESSAGE, + List.of(new JavaFileScannerContext.Location(SECONDARY_MESSAGE, jumpStatement)), + null); + return; + } + if (isUnconditionalJump(statement)) { + jumpStatement = statement; + } + visitNestedBlocks(statement); + } + } + + private static boolean isUnconditionalJump(StatementTree statement) { + return statement.is( + Tree.Kind.RETURN_STATEMENT, + Tree.Kind.THROW_STATEMENT, + Tree.Kind.BREAK_STATEMENT, + Tree.Kind.CONTINUE_STATEMENT); + } + + private void visitNestedBlocks(StatementTree statement) { + switch (statement.kind()) { + case IF_STATEMENT: + IfStatementTree ifStmt = (IfStatementTree) statement; + checkBlock(ifStmt.thenStatement()); + if (ifStmt.elseStatement() != null) { + checkBlock(ifStmt.elseStatement()); + } + break; + case FOR_STATEMENT: + checkBlock(((ForStatementTree) statement).statement()); + break; + case FOR_EACH_STATEMENT: + checkBlock(((ForEachStatement) statement).statement()); + break; + case WHILE_STATEMENT: + checkBlock(((WhileStatementTree) statement).statement()); + break; + case DO_STATEMENT: + checkBlock(((DoWhileStatementTree) statement).statement()); + break; + case SYNCHRONIZED_STATEMENT: + checkStatements(((SynchronizedStatementTree) statement).block().body()); + break; + case LABELED_STATEMENT: + checkBlock(((LabeledStatementTree) statement).statement()); + break; + case TRY_STATEMENT: + visitTryStatement((TryStatementTree) statement); + break; + case SWITCH_STATEMENT: + visitSwitchStatement((SwitchStatementTree) statement); + break; + default: + break; + } + } + + private void checkBlock(StatementTree statement) { + if (statement.is(Tree.Kind.BLOCK)) { + checkStatements(((BlockTree) statement).body()); + } else { + // Single-statement body: no unreachable siblings possible in this block, + // but we still recurse into any nested blocks it may contain + visitNestedBlocks(statement); + } + } + + private void visitTryStatement(TryStatementTree tryStatement) { + checkStatements(tryStatement.block().body()); + for (CatchTree catchTree : tryStatement.catches()) { + checkStatements(catchTree.block().body()); + } + if (tryStatement.finallyBlock() != null) { + checkStatements(tryStatement.finallyBlock().body()); + } + } + + private void visitSwitchStatement(SwitchStatementTree switchStatement) { + for (CaseGroupTree caseGroup : switchStatement.cases()) { + checkStatements(caseGroup.body()); + } + } +} diff --git a/java-checks/src/test/java/org/sonar/java/checks/S8795CheckTest.java b/java-checks/src/test/java/org/sonar/java/checks/S8795CheckTest.java new file mode 100644 index 00000000000..cf4ac8473b8 --- /dev/null +++ b/java-checks/src/test/java/org/sonar/java/checks/S8795CheckTest.java @@ -0,0 +1,33 @@ +/* + * SonarQube Java + * Copyright (C) SonarSource Sàrl + * mailto:info AT sonarsource DOT com + * + * You can redistribute and/or modify this program under the terms of + * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl. + * + * 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 Sonar Source-Available License for more details. + * + * You should have received a copy of the Sonar Source-Available License + * along with this program; if not, see https://sonarsource.com/license/ssal/ + */ +package org.sonar.java.checks; + +import org.junit.jupiter.api.Test; +import org.sonar.java.checks.verifier.CheckVerifier; + +import static org.sonar.java.checks.verifier.TestUtils.mainCodeSourcesPath; + +class S8795CheckTest { + + @Test + void test() { + CheckVerifier.newVerifier() + .onFile(mainCodeSourcesPath("checks/S8795CheckSample.java")) + .withCheck(new S8795Check()) + .verifyIssues(); + } +} diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8795.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8795.html new file mode 100644 index 00000000000..1329bcaea9c --- /dev/null +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8795.html @@ -0,0 +1,5 @@ +

TODO: Add description for rule S8795

+

Why is this an issue?

+

TODO: Explain why this is an issue

+

How to fix it

+

TODO: Explain how to fix it

diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8795.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8795.json new file mode 100644 index 00000000000..84e09daff0a --- /dev/null +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8795.json @@ -0,0 +1,14 @@ +{ + "title": "TODO: Title for S8795", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant/Issue", + "constantCost": "5min" + }, + "tags": [], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-8795", + "sqKey": "S8795", + "scope": "Main" +} \ No newline at end of file